p1227 关系运算图

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/83829757

差分约束系统

一.定义

差分约束系统(system of difference constraints),是求解关于一组变数的特殊不等式组之方法。如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。

二.详解

鉴于我是初学差分约束系统,对于概念其实并不是理解得很透彻, 不过等我理解透了,一定要写一篇解释差分约束系统的随笔
在此奉上几位大佬的博客
作者:英雄哪里出来
http://www.cppblog.com/menjitianya/archive/2015/11/19/212292.html

作者:HARD_UNDERSTAND
https://blog.csdn.net/hjt_fathomless/article/details/52463723

三.一道例题

1.题目(p1227)

描述 Description
给出一有向图,图中每条边都被标上了关系运算符‘<’,‘>’,‘=’。现在要给图中每个顶点标上一个大于等于0,小于等于k的某个整数使所有边上的符号得到满足。若存在这样的k,则求最小的k,若任何k都无法满足则输出NO。

例如下表中最小的k为2。

结点1>结点2
结点2>结点3
结点2>结点4
结点3=结点4

如果存在这样的k,输出最小的k值;否则输出‘NO’。

输入格式 Input Format
共二行,第一行有二个空格隔开的整数n和m。n表示G的结点个数,m表示G的边数,其中1<=n<=1000, 0<=m<=10000。全部结点用1到n标出,图中任何二点之间最多只有一条边,且不存在自环。
第二行共有3m个用空格隔开的整数,第3i-2和第3i-1(1<=i<=m)个数表示第i条边的顶点。第3i个数表示第i条边上的符号,其值用集合{-1,0,1}中的数表示:-1表示‘<’, 0 表示‘=’, 1表示‘>’。
输出格式 Output Format
仅一行,如无解则输出‘NO’;否则输出最小的k的值。
样例输入 Sample Input

4 4
1 2 -1 2 3 0 2 4 -1 3 4 -1

样例输出 Sample Output

2

时间限制 Time Limitation
各个测试点1s

2.一些感受

前些日子,由于找死,非得学最短路的四个算法(虽然现在可以说是学的差不多了),自我感觉良好,便想找道题练练,于是,打着写最短路的招牌,找到了这道题。
诶?,这道题怎么这么不对劲啊,想了半天,想了一个思路:既然他的路径上是关系运算符,且这是一个有向图,就说明他的节点是有着一个先后顺序的,想到这些,我决定打暴力:即在输入时按照关系运算符将节点编号排序,然后,将排在第一位的节点的值赋为0,然后递推下去,输出最后节点的值,。。。。。。。。。。。。。。。。。但是失败了,因为我无法处理相同关系的节点。。。。。。。。。。。。。。
所以,只好上网查题解,于是看到了一个从未见过的名词:差分约束系统!!!!!!!!!

3.题解

好吧,总之写一点题解吧(虽然我也不太懂),
我们可以考虑如果输入x,y,z,
若z== 1 , 那么x到y连一条权值1的边;
z== -1 ,y到x连一条权值1的边;
z==0 , 则两个点连一条权值0的无向边就好了;

代码如下(这是糖果一题中的不等关系,囊括了所有的不等关系)

for (int i=1;i<=m;++i)
	{
		int z=read(),x=read(),y=read();
		if (z==1) add(x,y,0),add(y,x,0);
		//z=1,表示第A个小朋友分到的糖果必须和第B个小朋友分到的糖果一样多
		else if (z==2)
		{//z=2, 表示第A个小朋友分到的糖果必须少于第B个小朋友分到的糖果
			if (x==y) { puts("-1"); exit(0); }
			add(x,y,1);
		}
		else if (z==3) add(y,x,0);
		//z=3,表示第A个小朋友分到的糖果必须不少于第B个小朋友分到的糖果
		else if (z==4)
		{//z=4,表示第A个小朋友分到的糖果必须多于第B个小朋友分到的糖果
			if (x==y) { puts("-1"); exit(0); }
			add(y,x,1);
		}
		else add(x,y,0);
		//z=5,表示第A个小朋友分到的糖果必须不多于第B个小朋友分到的糖果
	}

那么我们建立好了这个差分系统时,
2.我们就可以跑一边最长路了,
因为是最长路,
所以 dist[数组 ]初始化要是负无穷大
和最短路一样但是是对立的,
如果有一个正权环 , 那么我们肯定可以沿着这条正权环绕啊绕得到更长的最长路,
所以 不存在正确的k时就是不存在最长路的情况
比如a到b权1(a比b大) ,
b到c权1(b比c大),
而c到a也权1(c比a大) 自然是无解

所以我们就跑 SPFA最长路+判负环就好了
然后就是我们可以看到,
SPFA是求单源最短路径,
而这道题并没有明确的起点终点,
所以要得到每个点的d[]取最大值了,我们该怎么做到呢?
这里有一个小技巧了,
我们设置一个虚的源点0,
然后再从0到每个顶点连一条权值为0的有向边,

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1050;
const int maxm=20005;
const int inf=0x7fffffff;
struct rec
{
	int y,z,Next;
	rec()
	{
		y=z=Next=-1;
	}
}edge[maxm];
int n, m, tot, ans, head[maxn], cnt[maxn];
inline int read()
{
	int f=1,num=0;
	char ch=getchar();
	while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
	while (ch>='0'&&ch<='9') num=(num<<1)+(num<<3)+ch-'0', ch=getchar();
	return num*f;
}
void add(int x,int y,int z)
{
	edge[++tot].y=y,edge[tot].z=z,edge[tot].Next=head[x],head[x]=tot;
}
bool v[maxn];
int dist[maxn];
queue<int>q;
void spfa(int s)
{
	for (int i=1;i<=n;i++) dist[i]=-inf;
	memset(v,0,sizeof(v));
	dist[s]=0,v[s]=1;
	q.push(s);
	cnt[s]++;
	while (!q.empty())
	{
		int x=q.front();
		q.pop(),v[x]=0;
		for (int i=head[x];i!=-1;i=edge[i].Next)
		{
			int y=edge[i].y,z=edge[i].z;
			if (dist[y]<dist[x]+z)//求最长路
			{
				dist[y]=dist[x]+z;
				if (!v[y])
				{
					q.push(y),v[y]=1;
					if (++cnt[y]>n)
					{
						cout<<"NO"<<endl;
						exit(0);
					}
				}	
			} 
		}
	}
	for (int i=1;i<=n;i++)
		ans=max(ans,dist[i]);
	cout<<ans<<endl;
}
int main()
{
	memset(head,-1,sizeof(head));
	n=read(),m=read();
	int x,y,z;
	for (int i=1;i<=m;i++)
	{
		x=read(),y=read(),z=read();
		if (z==-1) add(y,x,1);
		else if (z==1) add(x,y,1);
		else if (z==0) add(x,y,0),add(y,x,0);//即构建一个差分约束系统
	}
	for (int i=1;i<=n;i++)
		add(0,i,0);//虚设源点
	spfa(0);
	return 0;
}

ok啦,这道题就解决了!!!!!!!!!

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/83829757