poj2983_差分约束
题意: 给出n个点的m条约束信息。每条信息表述为(P a b c)表示a在b北方距离c的位置,或者(V a b) 表示a在b北方1单位距离或者更远的位置。问是否可能存在符合以上m个要求的点。
分析:根据题意首先我们可以确定已知条件是一个差分约束系统,建立此系统:
p a b c:
xb-xa=c 即:
xb-xa >=c && xb-xa<=c -----> xa-xb<=-c && xb-xa<=c
v a b:
xb-xa>=1 ------> xa-xb<=-1.
然后将差分约束系统转化成约束图。
图单源最短路存在 ---->差分系统有解。
图中有负权回路------->差分系统不存在可行解。
下面给出三种实现:
1.spfa+邻接表
View Code
1 #include <iostream> 2 #include <stdio.h> 3 #include <memory.h> 4 #include <queue> 5 using namespace std; 6 //差分约束+spfa 7 //有重边,但这里不影响。 8 //2584K 766MS 9 const int maxe=400050; //这里对时间的影响不是很大 10 const int maxp=1005; 11 const int maxnum=(1<<30); 12 struct edge 13 { 14 int v; 15 int w; 16 int next; 17 }edge[maxe]; 18 typedef struct 19 { 20 bool use; 21 int d; 22 int cnt; 23 int pre; 24 }pp; 25 pp point[maxp]; 26 int p,e; 27 queue<int> q; 28 29 void Init() 30 { 31 int i; 32 for(i=0;i<=p;i++) 33 { 34 point[i].cnt=0; 35 point[i].d=maxnum; 36 point[i].pre=-1; 37 point[i].use=false; 38 } 39 int u,v,w; 40 char str[2]; 41 int index=1; 42 for(i=1;i<=e;i++) 43 { 44 scanf("%s",str); 45 if(str[0]=='P') 46 { 47 scanf("%d%d%d",&u,&v,&w); 48 edge[index].v=v; 49 edge[index].w=w; 50 edge[index].next=point[u].pre; 51 point[u].pre=index; 52 index++; 53 edge[index].v=u; 54 edge[index].w=-w; 55 edge[index].next=point[v].pre; 56 point[v].pre=index; 57 index++; 58 } 59 else 60 { 61 scanf("%d%d",&u,&v); 62 edge[index].v=u; 63 edge[index].w=-1; 64 edge[index].next=point[v].pre; 65 point[v].pre=index; 66 index++; 67 } 68 } 69 for(i=1;i<=p;i++) 70 { 71 edge[index].v=i; 72 edge[index].w=0; 73 edge[index].next=point[0].pre; 74 point[0].pre=index; 75 index++; 76 } 77 } 78 79 bool spfa(int u) 80 { 81 while(!q.empty()) 82 q.pop(); 83 point[u].d=0; 84 point[u].use=true; 85 point[u].cnt++; 86 q.push(u); 87 int t,i; 88 while(!q.empty()) 89 { 90 t=q.front(); 91 point[t].use=false; //!!! 92 q.pop(); 93 for(i=point[t].pre;i!=-1;i=edge[i].next) 94 { 95 int v=edge[i].v; 96 int w=edge[i].w; 97 if(point[v].d>point[t].d+w) 98 { 99 point[v].d=point[t].d+w; 100 if(!point[v].use) 101 { 102 point[v].use=true; 103 point[v].cnt++; 104 if(point[v].cnt>p+1) //这里应该是p+1,因为多了一个零点。但是我写成p也能过,为什么哦? 105 return false; 106 q.push(v); 107 } 108 } 109 } 110 } 111 return true; 112 } 113 114 int main() 115 { 116 while(scanf("%d%d",&p,&e)!=EOF) 117 { 118 Init(); 119 if(spfa(0)) 120 printf("Reliable\n"); 121 else 122 printf("Unreliable\n"); 123 } 124 return 0; 125 }
2.bellman-ford优化+邻接表简化
View Code
1 #include <iostream> 2 #include <stdio.h> 3 #include <memory.h> 4 using namespace std; 5 //差分约束+bellman优化+邻接表简化 6 //bellman-ford适合简化的邻接表 7 //2544K 532MS 8 const int maxnum=(1<<30); 9 const int maxe=400050; 10 const int maxp=1001; 11 struct edge 12 { 13 int u,v; 14 }edge[maxe]; 15 int weight[maxe]; //边权 16 int d[maxp]; //源点到各点的距离 17 int p,e; //e tips 18 int index; 19 20 void Init() 21 { 22 char ch; 23 int u,v,w,i; 24 for(i=0;i<=p;i++) //初始化源点到各点的距离 25 d[i]=maxnum; 26 index=1; 27 for(i=1;i<=e;i++) 28 { 29 getchar();//吃掉回车 30 scanf("%c",&ch); 31 if(ch=='P') //清晰边权,即A、B间距离确定,建立双向边 32 { 33 scanf("%d%d%d",&u,&v,&w); 34 edge[index].u=u; 35 edge[index].v=v; 36 weight[index]=w; 37 index++; 38 edge[index].u=v; 39 edge[index].v=u; 40 weight[index]=-w; 41 index++; 42 } 43 else //模糊边权,即A、B间距离不确定,建立单向边 44 { 45 scanf("%d%d",&u,&v); 46 edge[index].u=v; 47 edge[index].v=u; 48 weight[index]=-1; 49 index++; 50 } 51 } 52 for(i=1;i<=p;i++) //构建虚拟零点 53 { 54 edge[index].u=0; 55 edge[index].v=i; 56 weight[index]=0; 57 index++; 58 } 59 } 60 61 bool bellman() //优化的bellman-ford 62 { 63 d[0]=0; 64 bool flag; 65 int i,j; 66 //因为加了一个零点,导致点的个数加一,一开始没有注意这个问题,WA了 67 for(i=0;i<p+1;i++) //这里没有优化的时候遍历|V|-1次,优化后遍历|V|次,考察|V|次后是否还可以优化,从而得出结果。 68 { 69 flag=false; 70 for(j=1;j<index;j++) 71 if(d[edge[j].u]==maxnum) 72 continue; 73 else if(d[edge[j].v]>d[edge[j].u]+weight[j]) 74 { 75 d[edge[j].v]=d[edge[j].u]+weight[j]; 76 flag=true; 77 } 78 if(!flag) 79 break; 80 } 81 if(!flag) 82 return true; 83 return false; 84 } 85 86 int main() 87 { 88 while(scanf("%d%d",&p,&e)!=EOF) 89 { 90 Init(); 91 // for(i=1;i<index;i++) 92 // cout<<i<<" "<<edge[i].u<<" "<<edge[i].v<<" "<<weight[i]<<endl; 93 if(bellman()) 94 printf("Reliable\n"); 95 else 96 printf("Unreliable\n"); 97 } 98 return 0; 99 }
3.spfa+邻接表简化
View Code
1 #include <iostream> 2 #include <stdio.h> 3 #include <memory.h> 4 #include <queue> 5 using namespace std; 6 //差分约束,spfa,邻接表 7 //spfa适合原版邻接表 8 //2580K 1625MS 9 const int maxnum=(1<<30); 10 const int maxe=400050; 11 const int maxp=1001; 12 struct edge 13 { 14 int u,v; 15 }edge[maxe]; 16 int weight[maxe]; //边权 17 int d[maxp]; //源点到各点的距离 18 bool use[maxp]; 19 int cnt[maxp]; 20 int p,e; //e tips 21 int index; 22 queue<int> q; 23 24 void Init() 25 { 26 char ch; 27 int u,v,w,i; 28 for(i=0;i<=p;i++) //初始化源点到各点的距离 29 d[i]=maxnum; 30 memset(use,false,sizeof(use)); 31 memset(cnt,0,sizeof(cnt)); 32 33 index=1; 34 for(i=1;i<=e;i++) 35 { 36 getchar();//吃掉回车 37 scanf("%c",&ch); 38 if(ch=='P') //清晰边权,即A、B间距离确定,建立双向边 39 { 40 scanf("%d%d%d",&u,&v,&w); 41 edge[index].u=u; 42 edge[index].v=v; 43 weight[index]=w; 44 index++; 45 edge[index].u=v; 46 edge[index].v=u; 47 weight[index]=-w; 48 index++; 49 } 50 else //模糊边权,即A、B间距离不确定,建立单向边 51 { 52 scanf("%d%d",&u,&v); 53 edge[index].u=v; 54 edge[index].v=u; 55 weight[index]=-1; 56 index++; 57 } 58 } 59 for(i=1;i<=p;i++) //构建虚拟零点 60 { 61 edge[index].u=0; 62 edge[index].v=i; 63 weight[index]=0; 64 index++; 65 } 66 } 67 68 bool spfa(int u) 69 { 70 while(!q.empty()) 71 q.pop(); 72 d[u]=0; 73 use[u]=true; 74 cnt[u]++; 75 q.push(u); 76 int t,i; 77 while(!q.empty()) 78 { 79 t=q.front(); 80 use[t]=false; //!!! 81 q.pop(); 82 for(i=1;i<index;i++) //肯定是这里太费时间了。 83 { 84 if(edge[i].u==t) 85 { 86 int v=edge[i].v; 87 if(d[v]>d[t]+weight[i]) 88 { 89 d[v]=d[t]+weight[i]; 90 if(!use[v]) 91 { 92 use[v]=true; 93 cnt[v]++; 94 if(cnt[v]>p+1) 95 return false; 96 q.push(v); 97 } 98 } 99 } 100 } 101 } 102 return true; 103 } 104 105 int main() 106 { 107 while(scanf("%d%d",&p,&e)!=EOF) 108 { 109 Init(); 110 if(spfa(0)) 111 printf("Reliable\n"); 112 else 113 printf("Unreliable\n"); 114 } 115 return 0; 116 }
三种实现中最优的为第二种。具体见代码。
在2上的改进,不加虚拟节点了
View Code
1 #include <iostream> 2 #include <stdio.h> 3 #include <memory.h> 4 using namespace std; 5 //差分约束+bellman优化+邻接表简化 6 //bellman-ford适合简化的邻接表 7 //2532K 516MS 更好一点 8 // 9 const int maxnum=(1<<30); 10 const int maxe=400050; 11 const int maxp=1001; 12 struct edge 13 { 14 int u,v; 15 }edge[maxe]; 16 int weight[maxe]; //边权 17 int d[maxp]; //源点到各点的距离 18 int p,e; //e tips 19 int index; 20 21 void Init() 22 { 23 char ch; 24 int u,v,w,i; 25 for(i=0;i<=p;i++) //初始化源点到各点的距离 26 d[i]=0; 27 index=1; 28 for(i=1;i<=e;i++) 29 { 30 getchar();//吃掉回车 31 scanf("%c",&ch); 32 if(ch=='P') //清晰边权,即A、B间距离确定,建立双向边 33 { 34 scanf("%d%d%d",&u,&v,&w); 35 edge[index].u=u; 36 edge[index].v=v; 37 weight[index]=w; 38 index++; 39 edge[index].u=v; 40 edge[index].v=u; 41 weight[index]=-w; 42 index++; 43 } 44 else //模糊边权,即A、B间距离不确定,建立单向边 45 { 46 scanf("%d%d",&u,&v); 47 edge[index].u=v; 48 edge[index].v=u; 49 weight[index]=-1; 50 index++; 51 } 52 } 53 } 54 55 bool bellman() //优化的bellman-ford 56 { 57 bool flag; 58 int i,j; 59 for(i=0;i<p;i++) //这里没有优化的时候遍历|V|-1次,优化后遍历|V|次,考察|V|次后是否还可以优化,从而得出结果。 60 { 61 flag=false; 62 for(j=1;j<index;j++) 63 if(d[edge[j].v]>d[edge[j].u]+weight[j]) 64 { 65 d[edge[j].v]=d[edge[j].u]+weight[j]; 66 flag=true; 67 } 68 if(!flag) 69 break; 70 } 71 if(!flag) 72 return true; 73 return false; 74 } 75 76 int main() 77 { 78 while(scanf("%d%d",&p,&e)!=EOF) 79 { 80 Init(); 81 if(bellman()) 82 printf("Reliable\n"); 83 else 84 printf("Unreliable\n"); 85 // int i; 86 // for(i=1;i<=p;i++) 87 // cout<<d[i]<<endl; 88 } 89 return 0; 90 } 91 /* 92 3 4 93 P 1 2 1 94 P 2 3 1 95 V 1 3 96 P 1 3 1 97 5 5 98 V 1 2 99 V 2 3 100 V 3 4 101 V 4 5 102 V 3 5 103 */