2014.11.22 差分约束学习笔记
来源:互联网 发布:fc2最新域名2016 编辑:程序博客网 时间:2024/06/12 00:45
同机房的fye,rivendile大神早就学了很久很久差分约束。。。本蒟蒻却颓了两晚noip2014。。。(orz fye神SD rank 8!!!)于是我觉得不能等了,于是。。。于是。。。
写了一天差分约束啊!!!11道题啊,手都疼了、、、
言归正传,先给出差分约束的定义
如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。求解差分约束系统,可以转化成图论的单源最短路径(或最长路径)问题。 (摘自百度百科)
其实蛮好懂的。举个例子,若给出a-b<=k1,b-c<=k2;a-c<=k3;那么a-c的最大值是多少,很明显是把三元组转化为求c->a的最短路。反之亦反.
那么,我们可以将给出xj-xi<=k1(1<=i,j<=n),求xn-x1的最大值,转化为求xn-x1的最短路
可以将给出xj-xi>=k1(1<=i,j<=n),求xn-x1的最小值,转化为求xn-x1的最长路.
特别地,当给出 xj-xi<k1可以转成xj-xi<=ki-1,将 xj-xi>k1可以转成xj-xi>=k1+1,在类似地求出最长(短)路 。
最长(短)路可以用spfa求出,但貌似也可用bellman-ford。。。开始做题!!
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- codevs 1768 种树2&&3 裸的差分约束系统,但有隐藏条件0<=s[i]-s[i-1]<=a[i]或1 然后将<=a[i]转为>=-a[i] code:
#include<iostream>#include<cstdio>#include<cstring>using namespace std;int point[500001],next[2000001];struct hp{int v,w;}e[2000001];int queue[500001],n,m,head,tail,dis[500001],a[500001];bool exist[500001];int main(){int j,c,num,i,x,y,w,now;scanf("%d%d",&n,&m); for (i=1;i<=n;++i) scanf("%d",&a[i]); for (i=1;i<=m;++i) { scanf("%d%d%d",&x,&y,&w); next[i]=point[x-1]; point[x-1]=i; e[i].v=y; e[i].w=w; }num=m;for (i=0;i<=n-1;++i) { num++; next[num]=point[i]; point[i]=num; e[num].v=i+1; e[num].w=0; num++; next[num]=point[i+1]; point[i+1]=num; e[num].v=i; e[num].w=-a[i+1]; }memset(dis,128,sizeof(dis));memset(exist,false,sizeof(exist));memset(queue,0,sizeof(queue)); head=0;tail=1; queue[tail]=0; exist[0]=true; dis[0]=0; while (head!=tail) { head=head%30000+1; now=queue[head]; exist[now]=false; j=point[now]; while (j!=0) { c=e[j].v;if (dis[c]<dis[now]+e[j].w) { dis[c]=dis[now]+e[j].w; if (!exist[c]) { tail=tail%30000+1; queue[tail]=c; exist[c]=true; } }j=next[j]; } }cout<<dis[n]<<endl;}
一道裸的poj差分约束 3159 Candies,题意不再赘述,code:
#include<iostream>#include<cstdio>#include<cstring>using namespace std;struct hp{int v,w;}a[150001];int point[30001],n,m,next[150001],s[30001];long long dis[30001];bool exist[30001];using namespace std;int main(){int x,y,z,c,i,top,j,now;scanf("%d%d",&n,&m);for (i=1;i<=m;++i) { scanf("%d%d%d",&x,&y,&z); next[i]=point[x]; point[x]=i;a[i].v=y; a[i].w=z; }memset(exist,false,sizeof(exist));memset(dis,127,sizeof(dis));memset(s,0,sizeof(s)); top=1; s[1]=1; exist[1]=true; dis[1]=0;while (top>0) { now=s[top]; top--; j=point[now]; exist[now]=false; while (j!=0) { c=a[j].v; if (dis[c]>dis[now]+a[j].w) { dis[c]=dis[now]+a[j].w; if (!exist[c]) { top++; s[top]=c; exist[c]=true; } } j=next[j]; } }printf("%lld\n",dis[n]);}两道类似于种树2、3的poj1716&&1201 intervals&&integer intervals 注意隐含条件与转化(0<=s[i]-s[i-1]<=1)code(intervals):
#include<iostream>#include<cstdio>#include<cstring>using namespace std;int point[500001],next[2000001];struct hp{int v,w;}e[2000001];int queue[500001],n,m,head,tail,dis[500001],a[500001];bool exist[500001];int main(){int j,c,num,i,x,y,w,now,maxn,minn;scanf("%d",&m); minn=2100000000; maxn=0;for (i=1;i<=m;++i) { scanf("%d%d%d",&x,&y,&w); maxn=max(maxn,y); minn=min(minn,x); next[i]=point[x-1]; point[x-1]=i; e[i].v=y; e[i].w=w; }num=m;for (i=minn-1;i<=maxn-1;++i) { num++; next[num]=point[i]; point[i]=num; e[num].v=i+1; e[num].w=0; num++; next[num]=point[i+1]; point[i+1]=num; e[num].v=i; e[num].w=-1; }memset(dis,128,sizeof(dis));memset(exist,false,sizeof(exist));memset(queue,0,sizeof(queue)); head=0;tail=1; queue[tail]=minn-1; exist[minn-1]=true; dis[minn-1]=0; while (head!=tail) { head=head%30000+1; now=queue[head]; exist[now]=false; j=point[now]; while (j!=0) { c=e[j].v;if (dis[c]<dis[now]+e[j].w) { dis[c]=dis[now]+e[j].w; if (!exist[c]) { tail=tail%30000+1; queue[tail]=c; exist[c]=true; } }j=next[j]; } }cout<<dis[maxn]<<endl;}poj 3169 Layout 其实很水,只不过出现了判断环,存储一个点进队次数 当其>n时,出现了正(负)环,注意隐含条件 code:
#include<iostream>#include<cstring>#include<cstdio>using namespace std;struct hp{int v,w;}a[100000]; int queue[1001],num[1001],dis[1001],head,tail;int point[1001],next[100001],maxn;bool exist[1001];int main(){bool ff=false;int numx,n,mi,mj,i,j,now,c,x,y,z;freopen("layout.in","r",stdin);freopen("layout.out","w",stdout);scanf("%d%d%d",&n,&mi,&mj);numx=0;for (i=1;i<=mi;++i) { numx++; scanf("%d%d%d",&x,&y,&z);next[numx]=point[x]; point[x]=numx;a[numx].v=y; a[numx].w=z; }for (i=1;i<=mj;++i) { numx++; scanf("%d%d%d",&x,&y,&z); next[numx]=point[y]; point[y]=numx; a[numx].v=x; a[numx].w=-z; }for (i=2;i<=n;++i) { numx++; next[numx]=point[i]; point[i]=numx; a[numx].v=i-1; a[numx].w=0; }memset(queue,0,sizeof(queue)); memset(exist,false,sizeof(exist));memset(dis,127,sizeof(dis)); maxn=dis[0]; memset(num,0,sizeof(num));head=0;tail=1; queue[tail]=1; exist[1]=true; num[1]++; dis[1]=0;while (head!=tail) { head=head%1000+1; now=queue[head]; exist[now]=false; j=point[now]; while (j!=0) { c=a[j].v; if (dis[c]>dis[now]+a[j].w) { dis[c]=dis[now]+a[j].w; if (!exist[c]) { tail=tail%1000+1; queue[tail]=c; num[c]++; exist[c]=true; if (num[c]>n) ff=true; } } if (ff) break; j=next[j]; } }if (ff) cout<<-1<<endl;else { if (dis[n]>=maxn) cout<<-2<<endl; else cout<<dis[n]<<endl; }fclose(stdin); fclose(stdout);}
poj 1364 king,很水的一道差分约束,类似前缀和的处理,但有可能出现图不连通的情况,所以要作连通块次数遍spfa,如果出现环,则输出successful conspiracy,正常返回输出lamentable kingdom code:
#include<iostream>#include<cstdio>#include<cstring>using namespace std;struct hp{int v,w;}a[10001];int queue[10001],head,tail,num[10001],dis[10001];int point[10001],next[100001],n,m;bool exist[10001],f[10001];int main(){char kind[2]; bool ff;int i,j,c,now,x,t,k;scanf("%d",&n);while (n!=0) { memset(a,0,sizeof(a)); memset(point,0,sizeof(point)); memset(next,0,sizeof(next)); scanf("%d",&m); for (i=1;i<=m;++i) { cin>>x>>t>>kind>>k; if (kind[0]=='g') { next[i]=point[x-1]; point[x-1]=i; a[i].v=x+t; a[i].w=k+1; } if (kind[0]=='l') { next[i]=point[x+t]; point[x+t]=i; a[i].v=x-1; a[i].w=1-k; } }memset(f,false,sizeof(f)); ff=false;for (i=1;i<=n;++i) if (!f[i]) { memset(queue,0,sizeof(queue)); memset(num,0,sizeof(num)); memset(dis,128,sizeof(dis)); memset(exist,false,sizeof(exist)); head=0; tail=1; dis[i]=0; queue[tail]=i; num[i]++; exist[i]=true; f[i]=true; while (head!=tail) { head=head%10000+1; now=queue[head]; exist[now]=false; j=point[now]; while (j!=0) { c=a[j].v; if (dis[c]<dis[now]+a[j].w) { dis[c]=dis[now]+a[j].w; f[c]=true; if (!exist[c]) { exist[c]=true; num[c]++; tail=tail%10000+1; queue[tail]=c; if (num[c]>n) { ff=true; break; } } } if (ff) break; j=next[j]; } if (ff) break; } if (ff) break; }if (ff) printf("successful conspiracy\n");else printf("lamentable kingdom\n");scanf("%d",&n); }}poj 2983 Is the Information Reliable
一道比较有难度的差分约束,首先,由于题目给出一种情况的是确切的距离,我们可以转化为k<=s[i]-s[j]<=k,进一步变号为{s[j]-s[i]>=-k,s[i]-s[j]>=k},另外一种只说明了某一点与另外一点有位置关系,对此可以转化为s[i]-s[j]>=0,最后跑一遍最长路即可 code:
#include<iostream>#include<cstring>#include<cstdio>using namespace std;struct hp{int v,w;}a[200001];int queue[1001],head,tail,num[1001],dis[1001];int point[1001],next[200001],n,m;bool exist[1001],f[1001];using namespace std;int main(){int i,c,numx,now,j,ai,b; bool ff; char kind;while (scanf("%d%d",&n,&m)==2) { numx=0; memset(point,0,sizeof(point)); memset(next,0,sizeof(next)); memset(a,0,sizeof(a)); for (i=1;i<=m;++i) { cin>>kind;if (kind=='P') { scanf("%d%d%d",&ai,&b,&c); numx++; next[numx]=point[b]; point[b]=numx; a[numx].v=ai; a[numx].w=c; numx++; next[numx]=point[ai]; point[ai]=numx; a[numx].v=b; a[numx].w=-c; } if (kind=='V') { scanf("%d%d",&ai,&b); numx++; next[numx]=point[b]; point[b]=numx; a[numx].v=ai; a[numx].w=1; } } memset(f,false,sizeof(f)); ff=false; for (i=1;i<=n;++i) if (f[i]==false) { memset(num,0,sizeof(num)); memset(queue,0,sizeof(queue)); memset(exist,false,sizeof(exist)); memset(dis,128,sizeof(dis)); head=0; tail=1; dis[i]=0; exist[i]=true; queue[tail]=i; num[i]++; f[i]=true; while (head!=tail) { head=head%1000+1; now=queue[head]; exist[now]=false; j=point[now]; while (j!=0) { c=a[j].v; if (dis[c]<dis[now]+a[j].w) { dis[c]=dis[now]+a[j].w; f[c]=true; if (!exist[c]) { exist[c]=true; tail=tail%1000+1; queue[tail]=c; num[c]++; if (num[c]>n) { ff=true; break; } } } if (ff) break; j=next[j]; } if (ff) break; } if (ff) break; } if (ff) printf("Unreliable\n"); else printf("Reliable\n"); }}COGS 月考统计 一道比较水的题,但题目要求求出最小值,却给出了s[i]-s[j]<=k的条件,因此需转化为s[j]-s[i]>=-k,另外图也可能不连通,可以加设超级源点0,使其与所有点连通,作为最底成绩0,跑一遍最长路即可。 code:
#include<iostream>#include<cstdio>#include<cstring>using namespace std;struct hp{int v,w;}a[50001];int queue[10000],head,tail,dis[10000];int point[10000],next[50001],n,m,num[10000];bool exist[10000];int main(){int i,x,y,z,j,c,now; bool ff=false;freopen("ExamStat.in","r",stdin); freopen("ExamStat.out","w",stdout); scanf("%d%d",&n,&m);for (i=1;i<=m;++i) { scanf("%d%d%d",&x,&y,&z); next[i]=point[x]; point[x]=i; a[i].v=y; a[i].w=-z; }for (i=1;i<=n;++i) { next[m+i]=point[0]; point[0]=m+i;a[m+i].v=i; a[m+i].w=0; }memset(num,0,sizeof(num)); memset(dis,128,sizeof(dis));memset(queue,0,sizeof(queue)); memset(exist,false,sizeof(exist));head=0; tail=1; queue[tail]=0; num[0]++; dis[0]=0; exist[0]=true;while (head!=tail) { head=head%10000+1; now=queue[head]; exist[now]=false; j=point[now]; while (j!=0) { c=a[j].v; if (dis[c]<dis[now]+a[j].w) { dis[c]=dis[now]+a[j].w; if (!exist[c]) { num[c]++; exist[c]=true; tail=tail%10000+1; queue[tail]=c; if (num[c]>n) { ff=true; break; } } } j=next[j]; if (ff) break; } if (ff) break; }if (ff) printf("SOMEONE LAY!");else { for (i=1;i<=n;++i) printf("%d ",dis[i]);printf("\n"); } fclose(stdin); fclose(stdout);}
以下两题需先循环起点,根据长度推出终点
COGS 01串题目较之前发生了变化,给出两个不相干的条件,a0<=s[i][0]-s[j][0]<=b0,a1<=s[i][1]-s[j][1]<=b1,但仔细观察可知,区间长度一定,可以将其全部转化为0的情况i-j+1-b1<=s[i][0]-s[j][0]<=i-j+1-a1,再就是隐含条件0<=s[i][0]-s[j][0]<=1,再都转化为<=,跑一遍最短路,输出每个点的1-(dis[i]-dis[i-1](零的个数))。。。code:
#include<iostream>#include<cstdio>#include<cstring>using namespace std;struct hp{int v,w;}a[10000];int queue[1001],head,tail,num[1001],dis[1001];int point[1001],next[10001],n,a0,b0,l0,a1,b1,l1;bool exist[1001];int main(){int i,nx=0,now,c,j; bool ff=false;freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout);scanf("%d%d%d%d%d%d%d",&n,&a0,&b0,&l0,&a1,&b1,&l1);for (i=1;i<=n;++i) { nx++; next[nx]=point[i-1]; point[i-1]=nx; a[nx].v=i; a[nx].w=1; nx++; next[nx]=point[i]; point[i]=nx; a[nx].v=i-1; a[nx].w=0; }for (i=1;i<=n-l0+1;++i) { nx++; next[nx]=point[i-1]; point[i-1]=nx; a[nx].v=i+l0-1; a[nx].w=b0;nx++; next[nx]=point[i+l0-1]; point[i+l0-1]=nx; a[nx].v=i-1; a[nx].w=-a0; }for (i=1;i<=n-l1+1;++i) { nx++; next[nx]=point[i-1]; point[i-1]=nx; a[nx].v=i+l1-1; a[nx].w=l1-a1;nx++; next[nx]=point[i+l1-1]; point[i+l1-1]=nx; a[nx].v=i-1; a[nx].w=b1-l1; } memset(queue,0,sizeof(queue)); memset(dis,127,sizeof(dis));memset(exist,false,sizeof(exist)); memset(num,0,sizeof(num)); ff=false;head=0; tail=1; queue[tail]=0; dis[0]=0; exist[0]=true; num[0]++;while (head!=tail) { head=head%1000+1; now=queue[head]; exist[now]=false; j=point[now]; while (j!=0) { c=a[j].v; if (dis[c]>dis[now]+a[j].w) { dis[c]=dis[now]+a[j].w; if (!exist[c]) { exist[c]=true; tail=tail%1000+1; queue[tail]=c; num[c]++; if (num[c]>n) { ff=true; break; } } } j=next[j]; if (ff) break; } if (ff) break; }if (ff) printf("-1\n");else { for (i=1;i<=n;++i) { if (dis[i]-dis[i-1]==1) printf("0"); else printf("1"); } printf("\n"); }fclose(stdin); fclose(stdout);}COGS 数列问题
基本同上一题,但给出的是s[i]-s[j]>0或s[i]-s[j]<0,所以转化为s[j]-s[i]<=-1或s[i]-s[j]<=-1,此题依旧可能不连通,连通块遍spfa。。。输出dis[i]-dis[i-1];
#include<iostream>#include<cstdio>#include<cstring>using namespace std;struct hp{int v,w;}a[2000];int queue[101],head,tail,dis[101],num[101];int point[101],next[2000],n,p,q;bool exist[101],f[101];int main(){int i,nx=0,now,j,c; bool ff=false;freopen("topoa.in","r",stdin); freopen("topoa.out","w",stdout);scanf("%d%d%d",&n,&p,&q);for (i=1;i<=n-p+1;++i) {nx++; next[nx]=point[i+p-1]; point[i+p-1]=nx; a[nx].v=i-1; a[nx].w=-1;}for (i=1;i<=n-q+1;++i) {nx++; next[nx]=point[i-1]; point[i-1]=nx; a[nx].v=i+q-1; a[nx].w=-1;}memset(f,0,sizeof(f));for (i=0;i<=n;++i) if (!f[i]) { { memset(queue,0,sizeof(queue)); memset(num,0,sizeof(num)); memset(exist,false,sizeof(exist)); head=0; tail=1; queue[tail]=i; if (i!=0)dis[i]=dis[i-1]; else dis[i]=0; num[i]++; exist[i]=true; f[i]=true; while (head!=tail) { head=head%100+1; now=queue[head]; exist[now]=false; j=point[now]; while (j!=0) { c=a[j].v; if (dis[c]>dis[now]+a[j].w) { dis[c]=dis[now]+a[j].w; f[c]=true; if (!exist[c]) { exist[c]=true; num[c]++; tail=tail%100+1; queue[tail]=c; if (num[c]>n) { ff=true; break; } } } j=next[j]; if (ff) break; } if (ff) break; } if (ff) break; }if (ff) printf("no\n");else { for (i=1;i<=n;++i) printf("%d ",dis[i]-dis[i-1]); printf("\n"); } fclose(stdin); fclose(stdout); }--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Lcomyn
2014.11.22&&23
- 2014.11.22 差分约束学习笔记
- 差分约束学习笔记
- 差分约束系统学习笔记
- |算法讨论|差分约束 学习笔记
- 最短路+差分约束学习笔记
- 12.20 差分约束学习笔记
- 差分约束笔记
- [笔记]: 差分约束
- 差分约束学习
- 差分约束 学习资料
- 差分约束的学习
- 差分约束学习(一)POJ1201
- 【图-差分约束】 差分约束
- 学习笔记----差分约束系统初步 POJ 2983 Is the Information Reliable?
- 学习笔记----差分约束系统 POJ 2983 Is the Information Reliable?
- 差分约束系统
- 差分约束系统
- 差分约束系统
- 为程序员准备的7个网站
- 设置聊天泡泡颜色以及添加描边的方式
- MCAPI学习笔记<一>——简介与Linux下示例程序测试
- Coursera Algorithm, Part2 Week2: Minimum Spanning Trees & Shortest Paths
- oracle存储过程简单实例 变量赋值 游标遍历
- 2014.11.22 差分约束学习笔记
- HEVC标准概览(翻译)
- datagrid改变url参数值
- C++标准库---智能指针auto_ptr初探
- OpenGL简单绘制游戏角色阴影
- 设置闹钟等本地通知的中级使用
- LeetCode OJ 之 Add Two Numbers (”两数“相加)
- 学习OGRE~~
- 设计模式-代理模式