题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6252
题意:给出n个点,i-1到i的距离是不确定的,给出x代表两个人的距离,现在给出m条约束条件,每次给出A,B,C,D代表第一个人在区间(A,B)段,第二个人在区间(C,D)段,两个人的距离始终是x保持不变,求出一组每两个点距离都符合的可行解
数据范围:1≤T≤30,1≤N,M≤2000,1≤X≤1e9,1≤A,B,C,D≤N,A≤B≤A+1,C≤D≤C+1
差分约束算法:可以解决y-x<=常数的问题,那么对于y-x>=常数可以变成x-y<=-常数,对于y-x<常数可以变成y-x<=常数-1,对于y-x=常数可以变成y-x<=常数&&x-y<=常数,那么按照减数向被减数连一条权值为常数的边,再定义一个超级源点向每个点连一条权值为0的边跑spfa的最短路dis数组里储存的便是答案,所有的dis数组中的值都加一个常数就是另一组解
思路:对于m条约束每次,c-b<=x建边,d-a>=x建边,若是a==b&&c==d,那么就是c-a==x建边,然后因为每两个连续的点的距离大于等于1,所以a[i]-a[(i-1)]>=1建边,最后再建一个超级源点(我选的n)对所有点连一条权值为0的边跑spfa即可dis数组中的差值就是答案。
#include <bits/stdc++.h>
using namespace std;
const int N=5e4+5;
const int INF=0x7fffffff;
struct p{int to,val;};
vector<p>G[N];
int n,m,t,x,dis[N],cnt[N];
bool vis[N];
void spfa(){
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
for(int i=0;i<n;i++)dis[i]=INF;
vis[n]=1;dis[n]=0;cnt[n]=1;
queue<int>Q;
while(!Q.empty())Q.pop();
Q.push(n);
bool flag=1;
while(!Q.empty()){
int u=Q.front();
Q.pop();
vis[u]=0;
for(int i=0;i<G[u].size();i++){
int to=G[u][i].to;
if(dis[to]>dis[u]+G[u][i].val){
dis[to]=dis[u]+G[u][i].val;
if(!vis[to]){
vis[to]=1;
Q.push(to);
if(++cnt[to]>n){flag=0;break;}
}
}
}
if(flag==0)break;
}
if(flag==0)printf(" IMPOSSIBLE\n");
else{
for(int i=1;i<n;i++)printf(" %d",dis[i]-dis[i-1]);
puts("");
}
}
int main(){
int cas=1;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&x);
for(int i=0;i<=n;i++)G[i].clear();
for(int i=1;i<n;i++){
G[i].push_back(p{i-1,-1});///反向连-1的边
G[n].push_back(p{i,0});
}///把点n设为超级源点
for(int i=1;i<=m;i++){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
a--,b--,c--,d--;
if(a==b&&c==d){
G[c].push_back(p{a,-x});
G[a].push_back(p{c,x});
}
else{
G[d].push_back(p{a,-(x+1)});
G[b].push_back(p{c,(x-1)});
}
}
printf("Case #%d:",cas++);
spfa();
}
return 0;
}