Codeforces Global Round 12 E. Capitalism【查分约束】
题意: 给你n个点,m条边的无向图,现在有m个限制.每个点都有自己的属性值dis
op= =1,说明dis[u]+1=dis[v];
op= =2,说明dis[u]+1=dis[v]或dis[v]+1=dis[u];
让你最大化max(dis[1-n])-min(dis[1-n]),不满足限制条件输出NO,否则输出属性方案。
思路: 看上去很明显的查分约束,首先是把等式转换成不等式,再根据三角不等式建边跑最短路。
dis[u]-dis[v]=-1
等价:dis[u]-dis[v]<=-1&&dis[u]-dis[v]>=-1
等价:dis[u]-dis[v]<=-1&&dis[v]-dis[u]<=1;
另一个式子同理:但要注意的是,这里是或的关系,所以式子要取并集
等价:dis[u]-dis[v]<=1&&dis[v]-dis[u]<=1
不等式怎么建图呢?对于dis[u]-dis[v]<=k不妨移项看看
dis[u]<=dis[v]+k;这不就是最短路松弛条件里面的运算符反过来吗?
假如v—>u k=w(v,u),那么在跑最短路的时候,不就恰好满足条件了吗?
因此我们建立v-u权值是1的边.枚举每个点跑最短路的dis数组就是属性,
然后记得判负环表示无解,再把最后的答案带回去check一遍(可能会出现约束奇环),确保满足条件。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e2+5;
const int M=2e3+5;
int dis[N][N];
vector<int>G[N];
pair<int,int>q1[M];
int n,m,st;
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++)dis[i][j]=1e9;
dis[i][i]=0;
}
for(int i=1,u,v,op; i<=m; i++) {
scanf("%d%d%d",&u,&v,&op);
if(op==1)dis[u][v]=1,dis[v][u]=-1;
else dis[u][v]=1,dis[v][u]=1;
q1[i]={
u,v};
}
for(int j=1; j<=n; j++) {
for(int i=1; i<=n; i++) {
for(int k=1; k<=n; k++) {
dis[i][k]=min(dis[i][k],dis[i][j]+dis[j][k]);
}
if(dis[i][i]<0)return 0*printf("NO\n");
}
}
int ans=0;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(dis[i][j]>ans) {
ans=dis[i][j];
st=i;
}
}
}
for(int i=1;i<=m;i++){
if(abs(dis[st][q1[i].first]-dis[st][q1[i].second])!=1)return 0*printf("NO\n");
}
printf("YES\n");
printf("%d\n",ans);
for(int i=1;i<=n;i++)printf("%d ",dis[st][i]);
printf("\n");
}