差分约束学习笔记

建约束图:
有 xi - xj <= a
那加一条从 j->i边权为a的有向边
然后新增人工定点连所有的点,权值为 0
注意SPFA或者bellman的解法只使用于小于等于的情况
把其他的符合都转化为 <= 负号就可以了
1.
SOJ 1541 01串  
c[i]表示 第i个位置之前有多少个1出现
然后约束条件
C[i] - C[i-1] <= 1 (1<=i<=N)
C[i-1] - C[i] <= 0 (1<=i<=N)
C[i-L1] - C[i] <= -A1 (i-L1>=0)
C[i] - C[i-L1] <= B1 (i-L1>=0)
C[i] - C[i-L0] <= L0-A0 (i-L0>=0)
C[i-L0] - C[i] <= B0-L0 (i-L0>=0)
前两个隐藏条件不要忘记了
然后建约束图就可以了

SPOJ 1451
   
     
1 #
2   /*
3 #
4 * =====================================================================================
5 #
6 *
7 #
8 * Filename: Bellman_ford.cpp
9 #
10 *
11 #
12 * Description: system of difference constraints
13 #
14 *
15 #
16 * Version: 1.0
17 #
18 * Created: 2011年03�01� 14�14�24�
19 #
20 * Revision: none
21 #
22 * Compiler: gcc
23 #
24 *
25 #
26 * Author: ronaflx
27 #
28 * Company: hit-acm-group
29 #
30 *
31 #
32 * =====================================================================================
33 #
34   */
35 #
36 #include < iostream >
37 #
38 #include < cstring >
39 #
40 #include < cstdio >
41 #
42 #include < vector >
43 #
44   using namespace std;
45 #
46   const int SIZE = 10110 ;
47 #
48   const int INF = 100000000 ;
49 #
50 vector < pair < pair < int , int > , int > > edge;
51 #
52   int n, a0, a1, b0, b1, l0, l1;
53 #
54   int d[SIZE];
55 #
56
57 #
58   void Bellman_ford( int n, int p)
59 #
60 {
61 #
62 fill(d,d + n, INF);
63 #
64 d[n - 1 ] = 0 ;
65 #
66   for ( int i = 1 ;i < n;i ++ )
67 #
68 {
69 #
70 bool unfind = true ;
71 #
72 for (vector < pair < pair < int , int > , int > > ::iterator i = edge.begin(); i != edge.end(); i ++ )
73 #
74 {
75 #
76 int v = i -> first.first, u = i -> first.second, val = i -> second;
77 #
78 if (d[u] > d[v] + val)
79 #
80 {
81 #
82 unfind = false ;
83 #
84 d[u] = d[v] + val;
85 #
86 }
87 #
88 }
89 #
90 if (unfind)
91 #
92 break ;
93 #
94 }
95 #
96 for (vector < pair < pair < int , int > , int > > ::iterator i = edge.begin(); i != edge.end(); i ++ )
97 #
98 {
99 #
100 int v = i -> first.first, u = i -> first.second, val = i -> second;
101 #
102 if (d[u] > d[v] + val)
103 #
104 {
105 #
106 puts( " -1 " );
107 #
108 return ;
109 #
110 }
111 #
112 }
113 #
114 for ( int i = 1 ;i <= p;i ++ )
115 #
116 printf( " %d " , d[i] - d[i - 1 ]);
117 #
118 puts( "" );
119 #
120 }
121 #
122 int main()
123 #
124 {
125 #
126 while (scanf( " %d %d %d %d %d %d %d " , & n, & a0, & b0, & l0, & a1, & b1, & l1) == 7 )
127 #
128 {
129 #
130 edge.clear();
131 #
132 for ( int i = 1 ;i + l0 - 1 <= n;i ++ )
133 #
134 {
135 #
136 edge.push_back(make_pair(make_pair(i - 1 , i + l0 - 1 ), l0 - a0));
137 #
138 edge.push_back(make_pair(make_pair(i + l0 - 1 , i - 1 ), b0 - l0));
139 #
140 }
141 #
142 for ( int i = 1 ;i + l1 - 1 <= n;i ++ )
143 #
144 {
145 #
146 edge.push_back(make_pair(make_pair(i - 1 , i + l1 - 1 ), b1));
147 #
148 edge.push_back(make_pair(make_pair(i + l1 - 1 , i - 1 ), - a1));
149 #
150 }
151 #
152 for ( int i = 1 ;i <= n;i ++ )
153 #
154 {
155 #
156 edge.push_back(make_pair(make_pair(i - 1 , i), 1 ));
157 #
158 edge.push_back(make_pair(make_pair(i, i - 1 ), 0 ));
159 #
160 }
161 #
162 for ( int i = 0 ;i <= n;i ++ )
163 #
164 edge.push_back(make_pair(make_pair(n + 1 , i), 0 ));
165 #
166 Bellman_ford(n + 2 , n);
167 #
168 }
169 #
170 return 0 ;
171 #
172 }

2.
POJ 3169 差分约束系统
很好的一道题可惜数据太弱了
千万不要忘记隐藏的约束条件xi - x(i - 1) >= 0
此题需要用不加新点的SPFA或者bellman求最短路
设d[1] = 0;如果d[n] == INF;那么表示d[n]的位置不受约束条件的影响返回-2就可以了 如果有负环返回-1,否则返回d[n];
至于为什么是最短路是解,我只是试了几个例子证明了一下,具体的证明还有待大牛给出。
差分约束要求输出解的,我做的都一塌糊涂…………
各种牛给出了最长路求最小解,最短路求最大解的结论


关于加新点的特性,算法导论的习题中有一个结论
用该方法求出的最短路有 任意xi <= 0(因为有一个新源点到原图中点边权0的边,所以最短路不可能大于0),所以此题不可以加新的点
且sum(xi) 最大,因为每个xi都是最短路,不可能再短了,所以其他解析都比改解系和小//我的证明功底太差了……
关于加新源点的个人理解是,加入了一组约束条件 xi - x0 <= 0
而x被我们定为0,就像上题我们把x1定为0一样,这样从一个限定的点出发解约束条件

//此题的代码仍感觉有bug,有待完善

自我感觉标准的解应该是先做一次差分约束判定,然后再做一次最短路才是正解!

POJ 3169
   
     
#include < iostream >
#include
< cstring >
#include
< cstdio >
#include
< algorithm >

using namespace std;
const int V = 1000 ;
const int E = 200000 ;
const int INF = 100000000 ;

struct edge
{
int v, val;
edge
* nxt;
}pool[E],
* pp, * g[V];

int d[V], q[V], cnt[V];
bool chk[V];
void initialize()
{
memset(g,
0 , sizeof (g));
pp
= pool;
}

void addedge( int u, int v, int val)
{
pp
-> v = v;
pp
-> nxt = g[u];
pp
-> val = val;
g[u]
= pp ++ ;
}

int SPFA( int n)
{
fill(d, d
+ n, INF);
memset(chk,
false , sizeof (chk));
memset(cnt,
0 , sizeof (cnt));
d[
0 ] = 0 ;
chk[
0 ] = true ;
q[
0 ] = 0 ;
cnt[
0 ] ++ ;
int tail = 0 ;
for ( int i = 0 ;i <= tail;i ++ )
{
int beg = q[i % n];
if (cnt[beg] > n)
return - 1 ;
for (edge * i = g[beg];i != NULL;i = i -> nxt)
{
if (d[i -> v] > d[beg] + i -> val)
{
d[i
-> v] = d[beg] + i -> val;
if ( ! chk[i -> v])
{
chk[i
-> v] = true ;
tail
++ ;
q[tail
% n] = i -> v;
cnt[i
-> v] ++ ;
}
}
}
chk[beg]
= false ;
}
return d[n - 1 ] == INF ? - 2 : d[n - 1 ];
}

int main()
{
int n, ml, md;
while (scanf( " %d %d %d " , & n, & ml, & md) == 3 )
{
initialize();
int u, v, val;
for ( int i = 0 ;i < ml;i ++ )
{
scanf(
" %d %d %d " , & u, & v, & val);
addedge(u
- 1 , v - 1 , val);
}
for ( int i = 0 ;i < md;i ++ )
{
scanf(
" %d %d %d " , & u, & v, & val);
addedge(v
- 1 , u - 1 , - val);
}
for ( int i = 1 ;i < n;i ++ )
addedge(i, i
- 1 , 0 );
printf(
" %d\n " , SPFA(n));
}
return 0 ;
}

3.
zoj 1508 || poj 1201
poj 1716同上面是一个类型的题

POJ 1201
   
     
1 #include < iostream >
2 #include < cstring >
3 #include < cstdio >
4 #include < queue >
5 using namespace std;
6 const int V = 50010 ;
7 const int E = 1000001 ;
8 const int INF = 10000000 ;
9
10 struct edges
11 {
12 int v, val;
13 edges * next;
14 } pool[E], * g[V], * pp;
15 int cnt[V], dist[V];
16 bool vst[V];
17
18 void initialize()
19 {
20 memset(g, 0 , sizeof (g));
21 pp = pool;
22 }
23
24 void addedge( int a, int b, int v)
25 {
26 pp -> v = b;
27 pp -> val = v;
28 pp -> next = g[a];
29 g[a] = pp ++ ;
30 }
31
32 bool SPFA( int n)
33 {
34 memset(cnt, 0 , sizeof (cnt));
35 memset(vst, false , sizeof (vst));
36 fill(dist, dist + n, INF);
37 dist[ 0 ] = 0 ;
38 cnt[ 0 ] ++ ;
39 vst[ 0 ] = true ;
40 queue < int > q;
41 q.push( 0 );
42 while ( ! q.empty())
43 {
44 int beg = q.front();
45 q.pop();
46 vst[beg] = false ;
47 for (edges * i = g[beg]; i != NULL; i = i -> next)
48 {
49 int tmp = i -> v, val = i -> val;
50 if (dist[tmp] > dist[beg] + val)
51 {
52 dist[tmp] = dist[beg] + val;
53 if ( ! vst[tmp])
54 {
55 vst[tmp] = true ;
56 cnt[tmp] ++ ;
57 q.push(tmp);
58 if (cnt[tmp] > n)
59 return false ;
60 }
61 }
62 }
63 }
64 return true ;
65 }
66 int n, m;
67
68 int main()
69 {
70 while (scanf( " %d " , & n) == 1 )
71 {
72 m = 0 ;
73 int a, b, maxn = - 1 , c;
74 initialize();
75 for ( int i = 0 ; i < n; i ++ )
76 {
77 scanf( " %d %d %d " , & a, & b, & c);
78 a += 2 , b += 2 ;
79 maxn = max(a, max(b, maxn));
80 addedge(a - 1 , b, - c);
81 }
82 for ( int i = 1 ; i <= maxn; i ++ )
83 {
84 addedge(i - 1 , i, 0 );
85 addedge(i, i - 1 , 1 );
86 }
87 SPFA(maxn + 1 );
88 printf( " %d\n " , - dist[maxn]);
89 }
90 return 0 ;
91 }

开始的时候蒙的建图,实际上是用了原图的负权图,负权的最短路,正好是正权的最长路所以AC了
一个标准的方法是:最长路算法
变换约束条件
C[b] - C[a - 1] >= c
C[i] - C[i + 1] >= -1
C[i + 1] - C[i] >= 0
建图如下

other building
   
     
int main()
{
while (scanf( " %d " , & n) == 1 )
{
m
= 0 ;
int a, b, maxn = - 1 , c;
initialize();
for ( int i = 0 ; i < n; i ++ )
{
scanf(
" %d %d %d " , & a, & b, & c);
a
+= 2 , b += 2 ;
maxn
= max(a, max(b, maxn));
addedge(a
- 1 , b, c);
}
for ( int i = 1 ; i <= maxn; i ++ )
{
addedge(i
- 1 , i, 0 );
addedge(i, i
- 1 , - 1 );
}
SPFA(maxn
+ 1 );
printf(
" %d\n " , dist[maxn]);
}
return 0 ;
}

注意此题同样不用新源点,这个两个题都保证了从0点的可达新,所以我认为不需要做代码是正确的,也是我怀疑我3169代码错误的原因


zoj 1455
zoj 1420 || poj1275
还未完成

差分约束学的还是稀里糊涂的……判定理解到比较到位,解的最大最小没太理解只能按照这样做了

POJ 3159
简单的差分约束,用最短路求解最大值
C[i]表示第i个人与第1个人的差,说白了就是固定第一个人为0
这题的重要性在于对于大规模的图,用栈的SPFA比队列的要快的多,因为可以加快负环中的点入栈次数向n逼近,还有一篇论文专门讨论了这个问题(具体名字忘记了)
HDU 3666 去年harbin现场赛的题
log之后就是水题了,没有什么最大解最小解的东西,就判定,就喜欢这样的题
WA了无数次
边开小了double忘记换了,正赛就废了……
还有卡常数……话说去年网络赛HEU大火呀……那网络赛都绝了……

HDU 3666
   
     
1 #include < iostream >
2 #include < cstring >
3 #include < cstdio >
4 #include < cmath >
5 #include < algorithm >
6 #include < stack >
7
8 using namespace std;
9 const int V = 1000 ;
10 const int E = 2000000 ;
11 const int SIZE = 401 ;
12 const double INF = 100000000 ;
13
14 struct edge
15 {
16 int v;
17 double val;
18 edge * nxt;
19 }pool[E], * pp, * g[V];
20
21 double d[V];
22 int cnt[V];
23 bool chk[V];
24 void initialize()
25 {
26 memset(g, 0 , sizeof (g));
27 pp = pool;
28 }
29
30 void addedge( int u, int v, double val)
31 {
32 pp -> v = v;
33 pp -> nxt = g[u];
34 pp -> val = val;
35 g[u] = pp ++ ;
36 }
37
38 int SPFA( int n)
39 {
40 fill(d, d + n, INF);
41 memset(chk, false , sizeof (chk));
42 memset(cnt, 0 , sizeof (cnt));
43 d[ 0 ] = 0 ;
44 chk[ 0 ] = true ;
45 cnt[ 0 ] ++ ;
46 stack < int > stk;
47 stk.push( 0 );
48 while ( ! stk.empty())
49 {
50 int beg = stk.top();
51 stk.pop();
52 if (cnt[beg] > n)
53 return false ;
54 for (edge * i = g[beg];i != NULL;i = i -> nxt)
55 {
56 if (d[i -> v] > d[beg] + i -> val)
57 {
58 d[i -> v] = d[beg] + i -> val;
59 if ( ! chk[i -> v])
60 {
61 chk[i -> v] = true ;
62 stk.push(i -> v);
63 cnt[i -> v] ++ ;
64 }
65 }
66 }
67 chk[beg] = false ;
68 }
69 return true ;
70 }
71
72 double matrix[SIZE][SIZE];
73 int main()
74 {
75 int n, m, l, u;
76 while (scanf( " %d %d %d %d " , & n, & m, & l, & u) == 4 )
77 {
78 initialize();
79 for ( int i = 1 ;i <= n;i ++ )
80 {
81 for ( int j = 1 ;j <= m;j ++ )
82 {
83 scanf( " %lf " , & matrix[i][j]);
84 addedge(j + n, i, log( double (u)) - log(matrix[i][j]));
85 addedge(i, j + n, log(matrix[i][j]) - log( double (l)));
86 }
87 }
88 for ( int i = 1 ;i <= n + m;i ++ )
89 addedge( 0 , i, 0 );
90 printf( " %s\n " , SPFA(n + m + 1 ) ? " YES " : " NO " );
91 }
92 return 0 ;
93 }

你可能感兴趣的:(学习笔记)