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 */

 

posted @ 2012-08-21 14:53  pushing my way  阅读(216)  评论(0编辑  收藏  举报