P1993 小K的农场-差分约束+spfa判断负环

题目描述

小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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值