题目描述
小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共m个),以下列三种形式描述:
农场a比农场b至少多种植了c个单位的作物,
农场a比农场b至多多种植了c个单位的作物,
农场a与农场b种植的作物数一样多。
但是,由于小K的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。
输入格式
第一行包括两个整数 n 和 m,分别表示农场数目和小 K 记忆中的信息数目。
接下来 m 行:
如果每行的第一个数是 1,接下来有 3 个整数 a,b,c,表示农场 a 比农场 b 至少多种植了 c 个单位的作物。
如果每行的第一个数是 2,接下来有 3 个整数 a,b,c,表示农场 a 比农场 b 至多多种植了 c 个单位的作物。如果每行的第一个数是 3,接下来有 2 个整数 a,b,表示农场 a 种植的的数量和 b 一样多。
输出格式
如果存在某种情况与小 K 的记忆吻合,输出“Yes”,否则输出“No”。
输入输出样例
输入 #1
3 3
3 1 2
1 1 3 1
2 2 3 2
输出 #1
Yes
说明/提示
对于 100% 的数据保证:1 ≤ n,m,a,b,c ≤ 10000。
题解:根据差分约束构建最短路图,再用spfa算法判断是否存在负环。
差分约束:Va-Vb<=c,则点b->a距离为c,以此为根据建图。
比如样例
1 1 3 1
2 2 3 2
则有V1-V3>=1转换得V3-V1<=-1(等于号可以忽视),则顶点1->3距离为-1
V2-V3<=2,则有顶点3->2距离为2
另外题目中顶点1-n都视为终点,需要去找到达这些点的最短路,在这个过程判断是否存在负环,所以需要新建一个顶点0作为源点,而且源点0到1-n所有点距离为0,完善建图,并以0为起点开始跑spfa。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=20100;
struct Node{
int v,w,next;
}edge[N];
int head[N],cnt;
void add_edge(int u,int v,int w)
{
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
//inqt为判断某个点被松弛的次数
int inqt[N],n,m;
queue<int>q;
int dis[N];
bool vis[N];
bool spfa()
{
for(int i=0;i<=n;i++)
dis[i]=1e9,vis[i]=false,inqt[i]=0;
while(!q.empty())
q.pop();
dis[0]=0;
q.push(0);
inqt[0]=1;
vis[0]=true;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=false;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
int w=edge[i].w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
if(!vis[v])
{
vis[v]=true;
inqt[v]++;
q.push(v);
//说明存在负环
if(inqt[v]>n)
{
return false;
}
}
}
}
}
return true;
}
int main()
{
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
cnt=0;
while(m--)
{
int q,x,y,z;
scanf("%d",&q);
scanf("%d%d",&x,&y);
if(q==3)
{
z=0;
}
else scanf("%d",&z);
if(q==3)
{
add_edge(x,y,0);
// add_edge(y,x,0);
}
else if(q==1)
add_edge(x,y,-z);
else add_edge(y,x,z);
}
//以0为源点建图
for(int i=1;i<=n;i++)
add_edge(0,i,0);
if(spfa())
puts("Yes");
else puts("No");
return 0;
}