http://codeforces.com/problemset/problem/366/D
题意:给出n个点,m条边,a,b,ll,rr分别代表点a,点b相连,点a和点b的区间范围(ll,rr),然后让你选择边相连接使得可以从点1到点n,并且所有被选择的边所共同覆盖的区间范围最大。
4 4
1 2 1 10
2 4 3 5
1 3 1 5
2 4 2 7
6
4个点,4条边,现在可以从1--2--4,存在两条路径,(1,10)--(3,5)或者(1,10)——(2,7),要选择这所共同覆盖的区间范围最大的路径,那么就是(1,10)——(2,7),所覆盖的区间范围为7-2+1
若是最大区间范围没有,输出-1;
思路:仔细思考过后,会发现,其实若存在最大共同覆盖区间(p),那么区间p的左极限必然是某个区间的左端点,区间p的右极限必然是某个区间的右端点,当然这“某个区间”,可能会是一个区间,也可能会是两个区间......
如此的话,我是对所有区间按ll排序,然后枚举rr区间,找ll,用并查集判断是否可以从点1到点n.....可是wa多次......至今没有弄明白,是哪个地方思路问题?
wa代码:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; struct node { int v1,v2; int x,y; //int len; } s[3*1005]; int father[3000]; int n,m; int vist[1005],flg1,flg2,ans,xx,yy; int cmp(const node a,const node b) { if(a.x<b.x) return 1; else return 0; } int find(int x) { int root,i=x; while(x!=father[x]) x=father[x]; root=x; x=i; while(x!=father[x]) { i=father[x]; father[x]=root; x=i; } return root; } int main() { while(scanf("%d%d",&n,&m)>0) { for(int i=0; i<m; i++) { scanf("%d%d%d%d",&s[i].v1,&s[i].v2,&s[i].x,&s[i].y); //s[i].len=s[i].y-s[i].x+1; } sort(s,s+m,cmp); ans=0; for(int i=0; i<m; i++) { for(int j=0; j<=n; j++) father[j]=j; for(int j=0;j<m;j++) { if(s[i].y<s[j].x) break; if(s[j].x>s[i].x) continue; if(s[i].x>s[j].y) continue; int s1=find(s[j].v1); int s2=find(s[j].v2); if(s1!=s2) { father[s1]=s2; } if(find(1)==find(n)) { if(ans<s[j].y-s[i].x+1) ans=s[j].y-s[i].x+1; //printf("%d %d ",s[i].x,s[i].y); } } } if(ans==0) printf("Nice work, Dima! "); else printf("%d ",ans); } return 0; }
然后,我依旧是对ll排序,但是这次是枚举rr来找ll,依旧是并查集判断是否联通1到n,额,ac了......有点不可思议,思考ing
ac代码:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; struct node { int v1,v2; int x,y; //int len; } s[3*1005]; int father[3000]; int n,m; int vist[1005],flg1,flg2,ans,xx,yy; int cmp(const node a,const node b) { if(a.x<b.x) return 1; else return 0; } int find(int x) { int root,i=x; while(x!=father[x]) x=father[x]; root=x; x=i; while(x!=father[x]) { i=father[x]; father[x]=root; x=i; } return root; } int main() { while(scanf("%d%d",&n,&m)>0) { for(int i=0; i<m; i++) { scanf("%d%d%d%d",&s[i].v1,&s[i].v2,&s[i].x,&s[i].y); //s[i].len=s[i].y-s[i].x+1; } sort(s,s+m,cmp); ans=0; for(int i=0; i<m; i++) { for(int j=0; j<=n; j++) father[j]=j; for(int j=0;j<m;j++) { if(s[i].y<s[j].x) break; if(s[i].y>s[j].y) continue; //if(s[j].x>s[i].x) //continue; int s1=find(s[j].v1); int s2=find(s[j].v2); if(s1!=s2) { father[s1]=s2; } //printf("%d %d ",s[j].v1,s[j].v2); if(find(1)==find(n)) { if(ans<s[i].y-s[j].x+1) ans=s[i].y-s[j].x+1; break; } } } if(ans==0) printf("Nice work, Dima! "); else printf("%d ",ans); } return 0; }
经过思考,我觉得wa代码是因为rr无序,也就是说,我只对ll排序了,枚举ll所找到的rr是无规律的,导致本来有些区间是应该会先连通点1到点n的,而因为rr无序,扰乱了.....所以wa,额,为了验证是不是这样的,便有了接下来的代码:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; struct node { int v1,v2; int x,y; //int len; } s[3*1005]; int father[3000]; int n,m; int vist[1005],flg1,flg2,ans,xx,yy; int cmp(const node a,const node b) { if(a.y>b.y) return 1; else return 0; } int find(int x) { int root,i=x; while(x!=father[x]) x=father[x]; root=x; x=i; while(x!=father[x]) { i=father[x]; father[x]=root; x=i; } return root; } int main() { while(scanf("%d%d",&n,&m)>0) { for(int i=0; i<m; i++) { scanf("%d%d%d%d",&s[i].v1,&s[i].v2,&s[i].x,&s[i].y); //s[i].len=s[i].y-s[i].x+1; } sort(s,s+m,cmp); ans=0; for(int i=0; i<m; i++) { for(int j=0; j<=n; j++) father[j]=j; for(int j=0;j<m;j++) { if(s[i].x>s[j].y) continue; if(s[i].y<s[j].x) continue; //if(s[i].x) if(s[j].x>s[i].x) continue; int s1=find(s[j].v1); int s2=find(s[j].v2); if(s1!=s2) { father[s1]=s2; } //printf("%d %d ",s[j].v1,s[j].v2); if(find(1)==find(n)) { if(ans<s[j].y-s[i].x+1) ans=s[j].y-s[i].x+1; break; } } } if(ans==0) printf("Nice work, Dima! "); else printf("%d ",ans); } return 0; }
ac了......真是这样的哈......
具体的来说,我觉得解一道题目,不仅仅只是ac了就可以的........
这道题目,对于区间的排序问题,有讲究的。比如说只有四个点的情况,1 2 1 10与2 4 2 7;
表示点1和点2相连,区间值为1--10,点2与点4相连,区间值为2--7;
现在按区间的ll从小到大排序:
s[0].v1=1 s[0].v2=2 s[0].ll=1 s[0].rr=10;
s[1].v1=2 s[1].v2=4 s[1].ll=2 s[1].rr=7;
若枚举ll来确定rr,那么就会出现10-2+1的输出,其实为什么会这样?自己在纸上动手画画就可以明白;
但枚举rr来确定ll,却不会产生这样的情况......
同理,若是按照区间rr从大到小排序,然后枚举ll来确定rr,也会是正确的.......至于为什么一种要从小到大,而另一种要从大到小,找两个这样的区间,一个区间被另一个区间包含,然后动手画画,就可以明白........