差分约束

介绍:http://imlazy.ycool.com/post.1702305.html      

性质:对于这种有一个未知数定死的差分约束系统,还有一个有趣的性质,   

那就是通过最短路径算法求出来的一组解当中,所有未知数都达到最大值。   

那么如果在一个未知数定死的情况下,要求其它所有未知数的最小值怎么办?只要反过来求最长路径就可以了。   

1.POJ 1201 Intervals

构图:对于一个区间[a, b]和c, 则有 c<=s(b) – s(a),可构造一长度为c的有向边b -> a,      

            为确保联通,0<=s(i+1) – s(i) <=1; 即有i+1 ->i 权值为0,i->i+1权值为-1的两条边。      

求最短路:求最大点到最小点的最短路。  

代码
#include <iostream>
#include
<cstdio>
#include
<cstring>
#include
<vector>
#include
<queue>
using namespace std;

struct Node
{
Node(){}
Node(
int pp, int ll):p(pp), l(ll){}
int p, l;
};
int n, maxv, minv;
vector
<Node>mp[50005];

void init()
{
int i;
maxv
= -1;
minv
= 60000;
for(i=0; i<=50000; ++i) mp[i].clear();
for(i=1; i<=n; ++i){
int a, b, c;
scanf(
"%d %d %d", &a, &b, &c);
++ b;
mp[b].push_back(Node(a, c));
if(b > maxv) maxv = b;
if(a < minv) minv = a;
}
for(i=minv; i<=maxv-1; ++i){
mp[i
+1].push_back(Node(i, 0));
mp[i].push_back(Node(i
+1, -1));
}
}

void spfa()
{
int i, j, u, v, w;
bool mark[50005];
int Min[50005];
queue
<int> Q;
for(i=minv; i<=maxv; ++i){
mark[i]
= 0;
Min[i]
= -1;
}
Q.push(maxv);
mark[maxv]
= 1;
Min[maxv]
= 0;
while(!Q.empty()){
u
= Q.front();
Q.pop();
mark[u]
= 0;
int sz = mp[u].size();
for(i=0; i<sz; ++i){
v
= mp[u][i].p;
w
= mp[u][i].l;
if(Min[v] < Min[u] + w){
Min[v]
= Min[u] + w;
if(!mark[v]){
Q.push(v);
mark[v]
= 1;
}
}
}
}
printf(
"%d\n", Min[minv]);
}

int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d", &n)!=EOF){
init();
spfa();
}
return 0;
}

2.poj 1716 Integer Intervals

和poj1201差不多

代码
#include <iostream>
#include
<cstdio>
#include
<cstring>
#include
<vector>
#include
<queue>
using namespace std;

struct Node
{
Node(){}
Node(
int pp, int ll):p(pp), l(ll){}
int p, l;
};
int n, maxv, minv;
vector
<Node>mp[10005];

void init()
{
int i;
maxv
= -1;
minv
= 60000;
for(i=0; i<=10000; ++i) mp[i].clear();
for(i=1; i<=n; ++i){
int a, b;
scanf(
"%d %d", &a, &b);
++ b;
mp[b].push_back(Node(a,
2));
if(b > maxv) maxv = b;
if(a < minv) minv = a;
}
for(i=minv; i<=maxv-1; ++i){
mp[i
+1].push_back(Node(i, 0));
mp[i].push_back(Node(i
+1, -1));
}
}

void spfa()
{
int i, j, u, v, w;
bool mark[10005];
int Min[10005];
queue
<int> Q;
for(i=minv; i<=maxv; ++i){
mark[i]
= 0;
Min[i]
= -1;
}
Q.push(maxv);
mark[maxv]
= 1;
Min[maxv]
= 0;
while(!Q.empty()){
u
= Q.front();
Q.pop();
mark[u]
= 0;
int sz = mp[u].size();
for(i=0; i<sz; ++i){
v
= mp[u][i].p;
w
= mp[u][i].l;
if(Min[v] < Min[u] + w){
Min[v]
= Min[u] + w;
if(!mark[v]){
Q.push(v);
mark[v]
= 1;
}
}
}
}
printf(
"%d\n", Min[minv]);
}

int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d", &n)!=EOF){
init();
spfa();
}
return 0;
}

3.poj 3169 Layout

假如b > a, s(b) – s(a) <=c,构造a -> b 有向边,权值为c。s(b) – s(a) >= c即s(a) – s(b) <=-c同理可以构造一有向边。    

spfa最短路,无法到达返回-2,有负回路返回-1,其余返回最短路的值。

代码
#include <iostream>
#include
<cstdio>
#include
<cstring>
#include
<vector>
#include
<queue>
using namespace std;

struct Node
{
Node(){}
Node(
int pp, int ll):p(pp), l(ll){}
int p, l;
};
int n, A, B;
vector
<Node>mp[1005];
const int inf = (1<<29);

void init()
{
int i;
scanf(
"%d %d", &A, &B);
for(i=1; i<=n; ++i) mp[i].clear();
for(i=1; i<=A; ++i){
int a, b, c;
scanf(
"%d %d %d", &a, &b, &c);
mp[a].push_back(Node(b, c));
}
for(i=1; i<=B; ++i){
int a, b, c;
scanf(
"%d %d %d", &a, &b, &c);
mp[b].push_back(Node(a,
-c));
}
}

int spfa()
{
int i, u, v, w;
bool mark[1005];
int Min[1005], cnt[1005];
queue
<int> Q;
for(i=1; i<=n; ++i){
mark[i]
= 0;
Min[i]
= inf;
cnt[i]
= 0;
}
Q.push(
1);
mark[
1] = 1;
Min[
1] = 0;
while(!Q.empty()){
u
= Q.front();
Q.pop();
mark[u]
= 0;
int sz = mp[u].size();
for(i=0; i<sz; ++i){
v
= mp[u][i].p;
w
= mp[u][i].l;
if(Min[v] > Min[u] + w){
Min[v]
= Min[u] + w;
if(!mark[v]){
Q.push(v);
mark[v]
= 1;
cnt[v]
++;
if(cnt[v] > n) return -1;
}
}
}
}
if(Min[n] == inf) return (-2);
else return Min[n];
}

int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d", &n)!=EOF){
init();
printf(
"%d\n", spfa());
}
return 0;
}

4.poj 1364 King

a[si] + a[si + 1] + a[si + 2] +…+a[si+ni] < k ,即sum[si+ni] – sum[si-1] < k,   

sum[si+ni] – sum[si-1] <= k – 1;从而构造出差分约束系统。 

代码
#include <iostream>
#include
<cstdio>
#include
<cstring>
#include
<vector>
#include
<queue>
using namespace std;

struct Node
{
Node(){}
Node(
int pp, int ll):p(pp), l(ll){}
int p, l;
};

int n;
vector
<Node> mp[105];

void intograph()
{
int m, si, ni, ki;
char ch[10];
int i;
for(i=0; i<=n; ++i) mp[i].clear();
scanf(
"%d", &m);
while(m --){
scanf(
"%d %d %s %d", &si, &ni, ch, &ki);
if(ch[0] == 'g'){
int u, v;
u
= si + ni;
v
= si - 1;
mp[u].push_back(Node(v,
-1-ki));
}
else {
int u, v;
u
= si - 1;
v
= si + ni;
mp[u].push_back(Node(v, ki
-1));
}
}
}

bool spfa()
{
int u, v, i, w;
int cnt[105], Min[105];
bool mark[105];
queue
<int> Q;
for(i=0; i<=n; ++i){
cnt[i]
= Min[i] = 0;
Q.push(i);
mark[i]
= 1;
}
while(!Q.empty()){
u
= Q.front();
Q.pop();
mark[u]
= 0;
int sz = mp[u].size();
for(i=0; i<sz; ++i){
v
= mp[u][i].p;
w
= mp[u][i].l;
if(Min[v] > Min[u] + w){
Min[v]
= Min[u] + w;
if(!mark[v]){
Q.push(v);
mark[v]
= 1;
cnt[v]
++;
if(cnt[v] > n) return 0;
}
}
}
}
return 1;
}

int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d", &n)==1 && n){
intograph();
if(spfa()) puts("lamentable kingdom");
else puts("successful conspiracy");
}
return 0;
}

5.poj3159 Candies

这题分析起来很清晰,只是时间卡得太紧了,vector,queue超时,把vector换成指针,queue改成数组,还要加io外挂。。。纠结了半天。。。 

代码
#include <iostream>
#include
<cstdio>
#include
<cstring>
using namespace std;

struct Node
{
int p, l;
Node
*next;
}
*mp[30005];

int n, m;
const int maxn = 30005;

void Read(int &x)
{
char ch;
x
= 0;
ch
= getchar();
while(!(ch >= '0' && ch <= '9')) ch = getchar();
while(ch >= '0' && ch <= '9'){
x
= x * 10 + ch - '0';
ch
= getchar();
}
}

void intograph()
{
int a, b, c;
int i;
for(i=0; i<=n; ++i) mp[i] = NULL;
while(m --){
Read(a); Read(b); Read(c);
Node
*temp = new Node;
temp
->p = b;
temp
->l = c;
temp
->next = mp[a];
mp[a]
= temp;
}
}

int spfa()
{
int u, v, i, w;
int Min[maxn];
bool mark[maxn];
int stack[maxn], top = 0;
for(i=1; i<=n; ++i){
Min[i]
= 1<<29;
mark[i]
= 0;
}
stack[top
++] = 1;
mark[
1] = 1;
Min[
1] = 0;
while(top){
u
= stack[--top];
mark[u]
= 0;
Node
*pp;
for(pp=mp[u]; pp; pp=pp->next){
v
= pp->p;
w
= pp->l;
if(Min[v] > Min[u] + w){
Min[v]
= Min[u] + w;
if(!mark[v]){
stack[top
++] = v;
mark[v]
= 1;
}
}
}
}
return Min[n];
}

int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d %d", &n, &m)==2){
intograph();
printf(
"%d\n", spfa());
}
return 0;
}

6.hdoj 3666 THE MATRIX PROBLEM

哈尔滨区域赛的原题,比赛时还不会差分约束。。。 

L<=a[i][j]*n[i]/m[j]<=U 

加个log变形为: log(L) <= log(a[i][j]) + log(n[i]) – log(m[j]) <= log(U);

代码
#include <iostream>
#include
<cstdio>
#include
<cstring>
#include
<cmath>
#include
<vector>
using namespace std;

struct Node
{
int p;
double l;
Node(){}
Node(
int pp, double ll):p(pp), l(ll){}
};
const int maxn = 805;
int n, m;
double U, L;
vector
<Node> mp[maxn];

void add_edge(int u, int v, double w)
{
mp[u].push_back(Node(v, w));
}

void intograph()
{
int i, j;
double a;
for(i=0; i<=n+m; ++i) mp[i].clear();
for(i=1; i<=n; ++i){
for(j=1; j<=m; ++j){
scanf(
"%lf", &a);
a
= log(a);
add_edge(n
+j, i, U-a);
add_edge(i, n
+j, a-L);
}
}
}

int spfa()
{
int u, v, i;
double w, Min[maxn];
int cnt[maxn];
bool mark[maxn];
int stack[maxn], top = 0;
for(i=1; i<=n+m; ++i){
Min[i]
= double(1<<29);
mark[i]
= 0;
cnt[i]
= 0;
}
stack[top
++] = 1;
mark[
1] = 1;
Min[
1] = 0;
while(top){
u
= stack[--top];
mark[u]
= 0;
int sz = mp[u].size();
for(i=0; i<sz; ++i){
v
= mp[u][i].p;
w
= mp[u][i].l;
if(Min[v] > Min[u] + w){
Min[v]
= Min[u] + w;
if(!mark[v]){
stack[top
++] = v;
mark[v]
= 1;
cnt[v]
++;
if(cnt[v] > (1.0*n+m)) return 0;
}
}
}
}
return 1;
}

int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d %d %lf %lf", &n, &m, &L, &U)==4){
L
= log(L);
U
= log(U);
intograph();
if(spfa()) puts("YES");
else puts("NO");
}
return 0;
}
posted on 2010-11-13 09:43  CrazyAC  阅读(269)  评论(0编辑  收藏  举报