专题训练之差分约束
推荐几个比较好的博客:https://blog.csdn.net/whereisherofrom/article/details/78922648 最短路和差分约束
http://www.cnblogs.com/void/archive/2011/08/26/2153928.html 差分约数系统详解
https://blog.csdn.net/xuezhongfenfei/article/details/8685313 差分约束系统
SPFA最短路模板:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=1010; 8 const int inf=1e9; 9 struct edge{ 10 int v,cost; 11 edge(int _v=0,int _cost=0):v(_v),cost(_cost) {} 12 }; 13 vector<edge>E[maxn]; 14 bool vis[maxn]; 15 int cnt[maxn],dist[maxn],s,n; 16 17 void addedge(int u,int v,int w) 18 { 19 E[u].push_back(edge(v,w)); 20 } 21 22 bool SPFA() 23 { 24 memset(vis,false,sizeof(vis)); 25 memset(cnt,0,sizeof(cnt)); 26 for ( int i=1;i<=n;i++ ) dist[i]=inf; 27 vis[s]=true; 28 dist[s]=0; 29 queue<int>que; 30 que.push(s); 31 cnt[s]=1; 32 while ( !que.empty() ) { 33 int u=que.front(); 34 que.pop(); 35 vis[u]=false; 36 for ( int i=0;i<E[u].size();i++ ) { 37 int v=E[u][i].v; 38 int cost=E[u][i].cost; 39 if ( dist[v]>dist[u]+cost ) { 40 dist[v]=dist[u]+cost; 41 if ( !vis[v] ) { 42 vis[v]=true; 43 que.push(v); 44 if ( ++cnt[v]>n ) return false; 45 } 46 } 47 } 48 } 49 return true; 50 }
最长路模板:即将边权取负,最后的结果取负即可
1.(HDOJ1384)http://acm.hdu.edu.cn/showproblem.php?pid=1384
题意:有n个区间,给出每个区间的左端点和右端点以及在该区间至少有多少个数在集合Z里面,现在让你求出集合Z里面最少元素个数。
分析:首先该问题是求最少的元素个数所以需要求最长路,所有不等式的符号都是>=。对于d[i]表示从[0,i]中至少有多少元素在Z中。所以给定[x,y]和z,有d[y]-d[x-1]>=z,因为数组下标可以为0,所有将整个坐标向右平移一位(即坐标都+1).注意隐藏条件对于每个位置来说最多有一个元素,最少没有元素(即:d[i]-d[i-1]>=0 && d[i]-d[i-1]<=1)。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=5e4+10; 8 const int maxm=5e4+5; 9 const int inf=1e9; 10 struct edge{ 11 int v,cost; 12 edge(int _v=0,int _cost=0):v(_v),cost(_cost) {} 13 }; 14 vector<edge>E[maxn]; 15 bool vis[maxn]; 16 int dist[maxn],s,n; 17 18 void addedge(int u,int v,int w) 19 { 20 E[u].push_back(edge(v,-w)); 21 } 22 23 void SPFA() 24 { 25 memset(vis,false,sizeof(vis)); 26 for ( int i=s;i<=n;i++ ) dist[i]=inf; 27 vis[s]=true; 28 dist[s]=0; 29 queue<int>que; 30 que.push(s); 31 while ( !que.empty() ) { 32 int u=que.front(); 33 que.pop(); 34 vis[u]=false; 35 for ( int i=0;i<E[u].size();i++ ) { 36 int v=E[u][i].v; 37 int cost=E[u][i].cost; 38 if ( dist[v]>dist[u]+cost ) { 39 dist[v]=dist[u]+cost; 40 if ( !vis[v] ) { 41 vis[v]=true; 42 que.push(v); 43 } 44 } 45 } 46 } 47 } 48 49 int main() 50 { 51 int N,i,j,k,x,y,z; 52 while ( scanf("%d",&N)!=EOF ) { 53 n=0; 54 s=maxm; 55 for ( i=0;i<=maxm;i++ ) E[i].clear(); 56 for ( i=1;i<=N;i++ ) { 57 scanf("%d%d%d",&x,&y,&z); 58 x++;y++; 59 n=max(n,y); 60 s=min(s,x); 61 addedge(x-1,y,z); 62 } 63 for ( i=s-1;i<=n;i++ ) { 64 addedge(i,i+1,0); 65 addedge(i+1,i,-1); 66 } 67 s--; 68 SPFA(); 69 printf("%d\n",-dist[n]); 70 } 71 return 0; 72 }
其他同类型题:(ZOJ2770)http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2770
题意:借用陆逊火烧联营为背景。每个帐篷最多a[i]个人,在[x,y]这个帐篷范围内的人数最少为z个,求所有帐篷最少有多少人
分析:d[i]表示前i个帐篷的总人数,设置超级源点s=n+1。有以下几个不等式d[y]-d[x-1]>=z d[i+1]-d[i]<=a[i+1] d[i]-d[s]>=0
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 typedef long long ll; 8 const ll maxn=1010; 9 const ll inf=1e9; 10 struct edge{ 11 ll v,cost; 12 edge(ll _v=0,ll _cost=0):v(_v),cost(_cost) {} 13 }; 14 vector<edge>E[maxn]; 15 bool vis[maxn]; 16 ll cnt[maxn],dist[maxn],s,n,a[maxn]; 17 18 void addedge(ll u,ll v,ll w) 19 { 20 E[u].push_back(edge(v,-w)); 21 } 22 23 bool SPFA() 24 { 25 memset(vis,false,sizeof(vis)); 26 memset(cnt,0,sizeof(cnt)); 27 for ( ll i=0;i<=n+1;i++ ) dist[i]=inf; 28 vis[s]=true; 29 dist[s]=0; 30 queue<ll>que; 31 que.push(s); 32 cnt[s]=1; 33 while ( !que.empty() ) { 34 int u=que.front(); 35 que.pop(); 36 vis[u]=false; 37 for ( ll i=0;i<E[u].size();i++ ) { 38 ll v=E[u][i].v; 39 ll cost=E[u][i].cost; 40 if ( dist[v]>dist[u]+cost ) { 41 dist[v]=dist[u]+cost; 42 if ( !vis[v] ) { 43 vis[v]=true; 44 que.push(v); 45 if ( ++cnt[v]>(n+2) ) return false; 46 } 47 } 48 } 49 } 50 return true; 51 } 52 53 int main() 54 { 55 ll m,i,j,k,x,y,z; 56 while ( scanf("%lld%lld",&n,&m)!=EOF ) { 57 s=n+1; 58 for ( i=0;i<=n+1;i++ ) E[i].clear(); 59 for ( i=1;i<=n;i++ ) scanf("%d",&a[i]); 60 for ( i=1;i<=m;i++ ) { 61 scanf("%lld%lld%lld",&x,&y,&z); 62 addedge(x-1,y,z); 63 } 64 for ( i=1;i<=n;i++ ) { 65 addedge(i,i-1,-a[i]); 66 } 67 for ( i=0;i<=n;i++ ) { 68 addedge(s,i,0); 69 } 70 bool flag=SPFA(); 71 if ( flag ) printf("%lld\n",-dist[n]); 72 else printf("Bad Estimations\n"); 73 } 74 return 0; 75 }
2.(HDOJ1531)http://acm.hdu.edu.cn/showproblem.php?pid=1531
题意: 问是否存在长度为n的序列满足m个条件. 每个条件描述某一段子序列和大于或者小于k。注意条件的表示si,ni为si到si+ni这个范围
分析:用最长路处理,设置超级源点n+1(注意一共有n+2个点,包括初始化和判断一个点的入队都要用n+2,而不是n来判断)。除了给定m个条件外,还需要将超级源点连向其他点,建边权为0的边
注意:对于>和<的处理,是先变形(即改变不等号的方向)在变成>=或者<=。同时注意建边的方向,是谁指向谁,边权是多少。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=110; 8 const int inf=1e9; 9 struct edge{ 10 int v,cost; 11 edge(int _v=0,int _cost=0):v(_v),cost(_cost) {} 12 }; 13 vector<edge>E[maxn]; 14 bool vis[maxn]; 15 int cnt[maxn],dist[maxn],s,n; 16 17 void addedge(int u,int v,int w) 18 { 19 E[u].push_back(edge(v,-w)); 20 } 21 22 bool SPFA() 23 { 24 memset(vis,false,sizeof(vis)); 25 memset(cnt,0,sizeof(cnt)); 26 for ( int i=0;i<=n+1;i++ ) dist[i]=inf; 27 vis[s]=true; 28 dist[s]=0; 29 queue<int>que; 30 que.push(s); 31 cnt[s]=1; 32 while ( !que.empty() ) { 33 int u=que.front(); 34 que.pop(); 35 vis[u]=false; 36 for ( int i=0;i<E[u].size();i++ ) { 37 int v=E[u][i].v; 38 int cost=E[u][i].cost; 39 if ( dist[v]>dist[u]+cost ) { 40 dist[v]=dist[u]+cost; 41 if ( !vis[v] ) { 42 vis[v]=true; 43 que.push(v); 44 if ( ++cnt[v]>n+2 ) return false; 45 } 46 } 47 } 48 } 49 return true; 50 } 51 52 int main() 53 { 54 int m,i,j,k,x,y,z; 55 char str[10]; 56 bool flag; 57 while ( scanf("%d",&n)!=EOF && n ) { 58 scanf("%d",&m); 59 for ( i=0;i<=n+1;i++ ) E[i].clear(); 60 while ( m-- ) { 61 scanf("%d%d%s%d",&x,&y,str,&z); 62 y+=x; 63 x--; 64 if ( str[0]=='g' ) addedge(x,y,z+1); 65 else if ( str[0]=='l' ) addedge(y,x,-z+1); 66 } 67 s=n+1; 68 for ( i=0;i<=n;i++ ) { 69 addedge(s,i,0); 70 } 71 flag=SPFA(); 72 if ( flag ) printf("lamentable kingdom\n"); 73 else printf("successful conspiracy\n"); 74 } 75 return 0; 76 }
3.(HDOJ1534)http://acm.hdu.edu.cn/showproblem.php?pid=1534
题意:题意:有n个项目,每个项目 i 必须连续花费时间time[i] 才能完成, 有4种约束:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=1010; 8 const int inf=1e9; 9 struct edge{ 10 int v,cost; 11 edge(int _v=0,int _cost=0):v(_v),cost(_cost) {} 12 }; 13 vector<edge>E[maxn]; 14 bool vis[maxn]; 15 int cnt[maxn],dist[maxn],s,n,a[maxn]; 16 17 void addedge(int u,int v,int w) 18 { 19 E[u].push_back(edge(v,-w)); 20 } 21 22 bool SPFA() 23 { 24 memset(vis,false,sizeof(vis)); 25 memset(cnt,0,sizeof(cnt)); 26 for ( int i=0;i<=n;i++ ) dist[i]=inf; 27 vis[s]=true; 28 dist[s]=0; 29 queue<int>que; 30 que.push(s); 31 cnt[s]=1; 32 while ( !que.empty() ) { 33 int u=que.front(); 34 que.pop(); 35 vis[u]=false; 36 for ( int i=0;i<E[u].size();i++ ) { 37 int v=E[u][i].v; 38 int cost=E[u][i].cost; 39 if ( dist[v]>dist[u]+cost ) { 40 dist[v]=dist[u]+cost; 41 if ( !vis[v] ) { 42 vis[v]=true; 43 que.push(v); 44 if ( ++cnt[v]>(n+1) ) return false; 45 } 46 } 47 } 48 } 49 return true; 50 } 51 52 int main() 53 { 54 int i,j,k,x,y,z,m,h=0; 55 char str[10]; 56 while ( scanf("%d",&n)!=EOF && n ) { 57 for ( i=0;i<=n;i++ ) E[i].clear(); 58 for ( i=1;i<=n;i++ ) scanf("%d",&a[i]); 59 while ( scanf("%s",str) ) { 60 if ( str[0]=='#' ) break; 61 scanf("%d%d",&x,&y); 62 if ( str[0]=='S' && str[2]=='F' ) { 63 addedge(y,x,a[y]); 64 } 65 else if ( str[0]=='S' && str[2]=='S' ) { 66 addedge(y,x,0); 67 } 68 else if ( str[0]=='F' && str[2]=='F' ) { 69 addedge(y,x,a[y]-a[x]); 70 } 71 else if ( str[0]=='F' && str[2]=='S' ) { 72 addedge(y,x,-a[x]); 73 } 74 } 75 s=0; 76 for ( i=1;i<=n;i++ ) addedge(s,i,0); 77 bool flag=SPFA(); 78 printf("Case %d:\n",++h); 79 if ( flag ) { 80 for ( i=1;i<=n;i++ ) printf("%d %d\n",i,-dist[i]); 81 } 82 else printf("impossible\n"); 83 printf("\n"); 84 } 85 return 0; 86 }
4.(HDOJ1535) http://acm.hdu.edu.cn/showproblem.php?pid=1535
题意:有m条路,n个点,起点为1,求起点到每个点的花费和每个点回起点的花费之和
分析:两次最短路,第二次是需要将边反向一下即可。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=1000010; 8 const int inf=1e9; 9 struct edge{ 10 int v,cost; 11 edge(int _v=0,int _cost=0):v(_v),cost(_cost) {} 12 }; 13 struct node{ 14 int x,y,z; 15 }arr[maxn]; 16 vector<edge>E[maxn]; 17 bool vis[maxn]; 18 int cnt[maxn],dist[maxn],s,n; 19 20 void addedge(int u,int v,int w) 21 { 22 E[u].push_back(edge(v,w)); 23 } 24 25 bool SPFA() 26 { 27 memset(vis,false,sizeof(vis)); 28 memset(cnt,0,sizeof(cnt)); 29 for ( int i=1;i<=n;i++ ) dist[i]=inf; 30 vis[s]=true; 31 dist[s]=0; 32 queue<int>que; 33 que.push(s); 34 cnt[s]=1; 35 while ( !que.empty() ) { 36 int u=que.front(); 37 que.pop(); 38 vis[u]=false; 39 for ( int i=0;i<E[u].size();i++ ) { 40 int v=E[u][i].v; 41 int cost=E[u][i].cost; 42 if ( dist[v]>dist[u]+cost ) { 43 dist[v]=dist[u]+cost; 44 if ( !vis[v] ) { 45 vis[v]=true; 46 que.push(v); 47 if ( ++cnt[v]>n ) return false; 48 } 49 } 50 } 51 } 52 return true; 53 } 54 int main() 55 { 56 int m,i,j,k,T,x,y,z,ans; 57 scanf("%d",&T); 58 while ( T-- ) { 59 scanf("%d%d",&n,&m); 60 s=1; 61 ans=0; 62 for ( i=1;i<=m;i++ ) scanf("%d%d%d",&arr[i].x,&arr[i].y,&arr[i].z); 63 for ( i=1;i<=n;i++ ) E[i].clear(); 64 for ( i=1;i<=m;i++ ) addedge(arr[i].x,arr[i].y,arr[i].z); 65 SPFA(); 66 for ( i=2;i<=n;i++ ) ans+=dist[i]; 67 for ( i=1;i<=n;i++ ) E[i].clear(); 68 for ( i=1;i<=m;i++ ) addedge(arr[i].y,arr[i].x,arr[i].z); 69 SPFA(); 70 for ( i=2;i<=n;i++ ) ans+=dist[i]; 71 printf("%d\n",ans); 72 } 73 return 0; 74 }
5.(HDOJ1317)http://acm.hdu.edu.cn/showproblem.php?pid=1317
题意:刚开始在第一房间能量为100,每个房间连有几个其他房间,进入每个房间都能获得当前房间的能量值(能量值可能为负),可以重复进入一个房间去获取能量。当能量<=0时或者到达最后一个(第n个)房间时退出。
分析:在整个过程中可能出现的情况总结:无法到达终点;在到达终点前能量耗尽;当在途径终点的路上出现正环则一定可以到达。采用SPFA+floyd,floyd传递闭包判断一个点能否到达终点。再用SPFA跑最大点权,每次转移时需判断从该点是否能到达终点,以及进入该点会不会耗尽能量,进入该点能否增大之前到达该点的能力值(初始值为-inf)。然后跑一边SPFA,当能到达终点或者在终点的途中出现正环输出true。否则输出false。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=110; 8 const int inf=1e9; 9 vector<int>E[maxn]; 10 bool vis[maxn],d[maxn][maxn]; 11 int cnt[maxn],dist[maxn],s,n,a[maxn]; 12 13 void floyd() 14 { 15 for ( int k=1;k<=n;k++ ) { 16 for ( int i=1;i<=n;i++ ) { 17 if ( d[i][k]==false ) continue; 18 for ( int j=1;j<=n;j++ ) { 19 if ( d[k][j]==false || d[i][j] ) continue; 20 d[i][j]=true; 21 } 22 } 23 } 24 } 25 26 bool SPFA() 27 { 28 memset(vis,false,sizeof(vis)); 29 memset(cnt,0,sizeof(cnt)); 30 for ( int i=1;i<=n;i++ ) dist[i]=-inf; 31 vis[s]=true; 32 dist[s]=100; 33 queue<int>que; 34 que.push(s); 35 cnt[s]=1; 36 while ( !que.empty() ) { 37 int u=que.front(); 38 que.pop(); 39 if ( u==n ) return true; 40 vis[u]=false; 41 for ( int i=0;i<E[u].size();i++ ) { 42 int v=E[u][i]; 43 int x=dist[u]+a[v]; 44 if ( dist[v]<x && x>0 && ( d[v][n] || v==n ) ) { 45 dist[v]=x; 46 if ( !vis[v] ) { 47 vis[v]=true; 48 que.push(v); 49 if ( ++cnt[v]>n ) return true; 50 } 51 } 52 } 53 } 54 return false; 55 } 56 57 int main() 58 { 59 int m,i,j,k,x,y,z; 60 while ( scanf("%d",&n)!=EOF && n!=-1 ) { 61 for ( i=1;i<=n;i++ ) E[i].clear(); 62 for ( i=1;i<=n;i++ ) { 63 scanf("%d%d",&a[i],&k); 64 for ( j=0;j<k;j++ ) { 65 scanf("%d",&x); 66 E[i].push_back(x); 67 } 68 } 69 memset(d,false,sizeof(d)); 70 for ( i=1;i<=n;i++ ) { 71 for ( j=0;j<E[i].size();j++ ) d[i][E[i][j]]=true; 72 } 73 s=1; 74 floyd(); 75 if ( !d[s][n] ) { 76 printf("hopeless\n"); 77 continue; 78 } 79 bool flag=SPFA(); 80 if ( flag ) printf("winnable\n"); 81 else printf("hopeless\n"); 82 } 83 return 0; 84 }
6.(HDOJ4109)http://acm.hdu.edu.cn/showproblem.php?pid=4109
题意:有n条指令和m条要求,每条要求(x,y,z)要求指令y至少在指令x后zs执行,问执行完全部指令最少需要的时间
分析:读清题目很重要!!设置超级源点s=n。dist[i]表示i在第几秒执行有以下几条不等式关系:dist[y]-dist[x]>=z dist[i]-dist[s]>=1(最早在第1s开始)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=1010; 8 const int inf=1e9; 9 struct edge{ 10 int v,cost; 11 edge(int _v=0,int _cost=0):v(_v),cost(_cost) {} 12 }; 13 vector<edge>E[maxn]; 14 bool vis[maxn]; 15 int cnt[maxn],dist[maxn],s,n,num[maxn]; 16 17 void addedge(int u,int v,int w) 18 { 19 E[u].push_back(edge(v,-w)); 20 } 21 22 bool SPFA() 23 { 24 memset(vis,false,sizeof(vis)); 25 memset(cnt,0,sizeof(cnt)); 26 for ( int i=0;i<=n;i++ ) dist[i]=inf; 27 vis[s]=true; 28 dist[s]=0; 29 queue<int>que; 30 que.push(s); 31 cnt[s]=1; 32 while ( !que.empty() ) { 33 int u=que.front(); 34 que.pop(); 35 vis[u]=false; 36 for ( int i=0;i<E[u].size();i++ ) { 37 int v=E[u][i].v; 38 int cost=E[u][i].cost; 39 if ( dist[v]>dist[u]+cost ) { 40 dist[v]=dist[u]+cost; 41 if ( !vis[v] ) { 42 vis[v]=true; 43 que.push(v); 44 if ( ++cnt[v]>(n+1) ) return false; 45 } 46 } 47 } 48 } 49 return true; 50 } 51 52 int main() 53 { 54 int m,i,j,k,x,y,z,maxx,ans; 55 while ( scanf("%d%d",&n,&m)!=EOF ) { 56 for ( i=0;i<=n;i++ ) E[i].clear(); 57 memset(num,0,sizeof(num)); 58 s=n; 59 ans=0; 60 for ( i=1;i<=m;i++ ) { 61 scanf("%d%d%d",&x,&y,&z); 62 addedge(x,y,z); 63 } 64 for ( i=0;i<n;i++ ) addedge(s,i,1); 65 SPFA(); 66 for ( i=0;i<n;i++ ) { 67 num[-dist[i]]++; 68 ans=max(-dist[i],ans); 69 } 70 printf("%d\n",ans); 71 } 72 return 0; 73 }
7.(HDOJ3666)http://acm.hdu.edu.cn/showproblem.php?pid=3666
题意:有一个N*M的矩阵每个矩阵有一个不超过1000的正整数,先有两个序列a[i]和b[j],对于矩阵中任意(i,j)位置上的数x,使得x=x*a[i]/b[j]在[l,u]的范围内.问是否存在这样的a序列和b序列
分析:现有不等式l<=x(i,j)*a[i]/b[j]<=u,进行变形得,l/x<=a[i]/b[j]<=u/x,因为在差分约束系统中未知量之间的关系是相减的,所以采用取对数的方式化除为剪使得问题得以求解。dist在[1,n]内表示a[i]的值,在[n+1,n+m]内表示的是b[i]的值。这里注意判断负环的条件是一个点入队列超过sqrt(N+M),可能是玄学优化。不然会TLE
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 #include<cmath> 7 using namespace std; 8 const int maxn=1005; 9 const int maxm=405; 10 const int inf=1e9; 11 struct edge{ 12 int v; 13 double cost; 14 edge(int _v=0,double _cost=0):v(_v),cost(_cost) {} 15 }; 16 vector<edge>E[maxn]; 17 bool vis[maxn]; 18 int cnt[maxn],s,n,mp[maxm][maxm]; 19 double dist[maxn]; 20 21 void addedge(int u,int v,double w) 22 { 23 E[u].push_back(edge(v,w)); 24 } 25 26 bool SPFA() 27 { 28 memset(vis,false,sizeof(vis)); 29 memset(cnt,0,sizeof(cnt)); 30 for ( int i=0;i<=n;i++ ) dist[i]=inf; 31 vis[s]=true; 32 dist[s]=0; 33 queue<int>que; 34 que.push(s); 35 cnt[s]=1; 36 while ( !que.empty() ) { 37 int u=que.front(); 38 que.pop(); 39 vis[u]=false; 40 for ( int i=0;i<E[u].size();i++ ) { 41 int v=E[u][i].v; 42 double cost=E[u][i].cost; 43 if ( dist[v]>dist[u]+cost ) { 44 dist[v]=dist[u]+cost; 45 if ( !vis[v] ) { 46 vis[v]=true; 47 que.push(v); 48 if ( ++cnt[v]>sqrt(n+1) ) return false; 49 } 50 } 51 } 52 } 53 return true; 54 } 55 56 int main() 57 { 58 int M,l,u,i,j,k,x,y,z,N; 59 double dis1,dis2; 60 while ( scanf("%d%d%d%d",&N,&M,&l,&u)!=EOF ) { 61 n=M+N; 62 s=0; 63 for ( i=0;i<=n;i++ ) E[i].clear(); 64 for ( i=1;i<=N;i++ ) { 65 for ( j=1;j<=M;j++ ) scanf("%d",&mp[i][j]); 66 } 67 for ( i=1;i<=N;i++ ) { 68 for ( j=1;j<=M;j++ ) { 69 x=i; 70 y=j+N; 71 dis1=(double)log(1.0*l/mp[i][j]); 72 dis2=(double)log(1.0*u/mp[i][j]); 73 addedge(y,x,dis2); 74 addedge(x,y,-dis1); 75 } 76 } 77 for ( i=1;i<=n;i++ ) addedge(s,i,0); 78 bool flag=SPFA(); 79 if ( flag ) printf("YES\n"); 80 else printf("NO\n"); 81 } 82 return 0; 83 }
8.(HDOJ1529)http://acm.hdu.edu.cn/showproblem.php?pid=1529
题意:超市在一天的每个时间段(以一个小时为单位)都有需要的最少工作人数。现有m个人,每人一个开始工作的时间a[i],每个人从开始工作算起连续工作8个小时。先求满足要求最少需要雇佣多少人。
分析:题目里r[0]表示[0,1)所需要的人数,我们现在定义a[1]为[0,1)所需要的人数,b[i]为在i-1时刻过来应聘的人数。对于dist[i]表示的含义是从[0,i)所需要的人数。此题的难点在于对于满足同一要求的不定方程需要分段考虑
现有三条不定方程:A.dist[i]-dist[i-8]>=a[i](24>=i>=8)(每个时间段内至少需要的员工数) B.dist[i]+dist[26]-dit[i+16]>=a[i](1<=i<8)(每个时间段需要的员工数) C.b[i]>=dist[i]-dist[i-1]>=0 (1<=i<=24)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=30; 8 const int inf=1e9; 9 struct edge{ 10 int v,cost; 11 edge(int _v=0,int _cost=0):v(_v),cost(_cost) {} 12 }; 13 vector<edge>E[maxn]; 14 bool vis[maxn],flag; 15 int cnt[maxn],dist[maxn],s,n,m,a[maxn],b[maxn]; 16 17 void addedge(int u,int v,int w) 18 { 19 E[u].push_back(edge(v,-w)); 20 } 21 22 bool SPFA() 23 { 24 memset(vis,false,sizeof(vis)); 25 memset(cnt,0,sizeof(cnt)); 26 for ( int i=0;i<=n;i++ ) dist[i]=inf; 27 vis[s]=true; 28 dist[s]=0; 29 queue<int>que; 30 que.push(s); 31 cnt[s]=1; 32 while ( !que.empty() ) { 33 int u=que.front(); 34 que.pop(); 35 vis[u]=false; 36 for ( int i=0;i<E[u].size();i++ ) { 37 int v=E[u][i].v; 38 int cost=E[u][i].cost; 39 if ( dist[v]>dist[u]+cost ) { 40 dist[v]=dist[u]+cost; 41 if ( !vis[v] ) { 42 vis[v]=true; 43 que.push(v); 44 if ( ++cnt[v]>(n+1) ) return false; 45 } 46 } 47 } 48 } 49 return true; 50 } 51 52 void init() 53 { 54 for ( int i=0;i<=24;i++ ) E[i].clear(); 55 } 56 57 bool judge(int mid) 58 { 59 init(); 60 for ( int i=1;i<=24;i++ ) { 61 addedge(i-1,i,0); 62 addedge(i,i-1,-b[i]); 63 if ( i>=8 ) addedge(i-8,i,a[i]); 64 else addedge(i+16,i,a[i]-mid); 65 } 66 return SPFA(); 67 } 68 69 int main() 70 { 71 int i,j,k,x,y,z,T,l,r,mid; 72 scanf("%d",&T); 73 while ( T-- ) { 74 n=24; 75 flag=false; 76 for ( i=1;i<=24;i++ ) scanf("%d",&a[i]); 77 scanf("%d",&m); 78 memset(b,0,sizeof(b)); 79 for ( i=1;i<=m;i++ ) { 80 scanf("%d",&x); 81 b[++x]++; 82 } 83 s=0; 84 l=0; 85 r=m; 86 while ( r-l>1 ) { 87 mid=(l+r)/2; 88 if ( judge(mid) ) { 89 flag=true; 90 r=mid; 91 } 92 else l=mid; 93 } 94 if ( flag ) printf("%d\n",r); 95 else printf("No Solution\n"); 96 } 97 return 0; 98 }
小结:对于差分约束的题目,题目往往描述的关系是不定方程。当求最小时,此时为>=,考虑最长路。当求最大时,此时为<=,考虑最短路。一定要首先明确dist[i]代表的含义是什么,起点是什么,除了题目表面描述的条件以外还有哪些附加的条件。