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

 

你可能感兴趣的:(差分约束)