差分约束题集:zoj 2770&poj1364&poj1275&poj3169

差分约束系统详解请戳:    差分约束详解

zoj2770

求最小值:(差分不等式a-b>=c,求最长路,得到最小值)

由于

①:Ai>=0   >>>>   Si-S(i-1)>=0

②:Ai<=a   >>>>  S(i-1)-Si>=-a

③:Si-S0>=0(0可以视为新增上的一个源点)

④:Sb-S(a-1)>=c

根据上公式就可以建立图了

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int dist[1010];
int n,m,a,b,c;
struct edge
{
	int to,cost;
}cur;
vector<edge>vec[1010];
inline void read(int &m)//int
{
	int x=0,f=1;char ch=getchar();//int
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	m=x*f;
}
bool spfa()  //(差分不等式a-b>=c,求最长路,得到最小值)
{
	int cnt[1010];bool vis[1010];
	for(int i=1;i<=n;i++)
		dist[i]=-1111111111;
	memset(cnt,0,sizeof(cnt));
	memset(vis,0,sizeof(vis));
	vis[0]=1;
	dist[0]=0;
	queue<int>que;
	que.push(0);
	while(!que.empty())
	{
		int v=que.front();
		que.pop();
		if(++cnt[v]>=n)
			return 0;
		vis[v]=0;
		for(int i=0;i<vec[v].size();i++)
		{
			edge temp=vec[v][i];
			if(dist[temp.to]<dist[v]+temp.cost)  //其实源点松弛后所有点的dist[]全部变为0了且全部入队。和另一个方法差不多
			{
				dist[temp.to]=dist[v]+temp.cost;
				if(!vis[temp.to])
				{
					vis[temp.to]=1;
					que.push(temp.to);
				}
			}
		}
	}
	return 1;
}
int main()
{
	while(~scanf("%d %d",&n,&m))
	{
		for(int i=0;i<=n;i++)
			vec[i].clear();
		for(int i=1;i<=n;i++)
		{
			read(a);
			cur.to=i;
			cur.cost=0;
			vec[i-1].push_back(cur);  //Ai>=0  Si-S(i-1)>=0     
			cur.to=i-1;
			cur.cost=-a;
			vec[i].push_back(cur); //Ai<=a   S(i-1)-Si>=-a
			cur.to=i;
			cur.cost=0;
			vec[0].push_back(cur);  //Si-S0>=0     //0点为新添上去的源点,与其他节点建立关系
		}
		for(int i=1;i<=m;i++)
		{
			read(a),read(b),read(c);
			cur.to=b;
			cur.cost=c;
			vec[a-1].push_back(cur);  //Sb-Sa-1>=c
		}
		if(spfa())
			printf("%d\n",dist[n]);
		else 
			printf("Bad Estimations\n");
	}
	return 0;
}


poj1364.

将>和<变成>=和<=.因为差分约束只限于>=和<=

同时加了一个超级源点保安图的连通性

这道题就两个很明显的公式。不用推也可以了。

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int n,m;
struct edge
{
	int to,cost;
}cur;
vector<edge>vec[110];
bool spfa()
{
	bool vis[110];
	int dist[110],cnt[110];
	memset(vis,0,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
	fill(dist,dist+110,10000000);
	vis[n+1]=1;
	dist[n+1]=0;
	queue<int>que;
	que.push(n+1);
	while(!que.empty())
	{
		int v=que.front();
		que.pop();
		vis[v]=0;
		if(++cnt[v]>n)return 1;  //不包括超级源点的话,实际上起作用的就n+1个节点
		for(int i=0;i<vec[v].size();i++)
		{
			edge temp=vec[v][i];
			if(dist[temp.to]>dist[v]+temp.cost)
			{
				dist[temp.to]=dist[v]+temp.cost;
				if(!vis[temp.to])
					vis[temp.to]=1,que.push(temp.to);
			}
		}
	}
	return 0;
}
int main()
{
	while(cin>>n&&n)
	{
		cin>>m;
		for(int i=0;i<=n+1;i++)
			vec[i].clear();
		while(m--)
		{
			int a,b,c;
			string st;
			cin>>a>>b>>st>>c;
			b+=a;
			if(st[0]=='l')  //全部转换成<=.....
			{
				cur.to=b;
				cur.cost=c-1;
				vec[a-1].push_back(cur);
			}
			else
			{
				cur.to=a-1;
				cur.cost=-c-1;
				vec[b].push_back(cur);
			}
		}
		for(int i=0;i<=n;i++)  //新加入的超级源点,与其他点相邻,距离为0
		{
			cur.to=i;
			cur.cost=0;
			vec[n+1].push_back(cur);
		}
		if(spfa())
			cout<<"successful conspiracy\n";
		else
			cout<<"lamentable kingdom\n";
	}
}


poj1275


其中Si-Sj这条公式是根据

①s[ I ]-s[ I-8 ]>=r[ I ], 8<=I<=23 
②s[ 23 ]+s[ I ]-s[ I+16 ]>=r[ I ], 0<=I<=7

整理而得

图片中的第三个公式S[23] - S[-1] >= sum;是必须的。

至于为什么请戳:为嘛是必须哒?

代码:

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int cas,n,k;
int r[30],num[30];
bool flag;
struct edge
{
	int to,cost;
}cur;
vector<edge>vec[30];
inline void read(int &m)//int
{
	int x=0,f=1;char ch=getchar();//int
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	m=x*f;
}
void BuildGraph(int sum)
{
	for(int i=0;i<=25;i++)
		vec[i].clear();
	cur.to=24;
	cur.cost=sum;
	vec[0].push_back(cur);
	for(int i=1;i<=24;i++)
	{
		cur.to=i;
		cur.cost=0;
		vec[i-1].push_back(cur);  //Si-S(i-1)>=0
		cur.to=i-1;
		cur.cost=-num[i];
		vec[i].push_back(cur);  //S(i-1)-Si>=-t[i]
	}
	for(int i=1;i<=8;i++)
	{
		cur.to=i;
		cur.cost=r[i]-sum;
		vec[i+16].push_back(cur);
	}
	for(int i=9;i<=24;i++)
	{
		cur.to=i;
		cur.cost=r[i];
		vec[i-8].push_back(cur);
	}
}
bool spfa(int sum)
{
	int cnt[30],dist[30];
	bool vis[30];
	for(int i=0;i<=25;i++)
	{
		cnt[i]=0;
		vis[i]=0;
		dist[i]=-11111111;
	}
	vis[0]=1;
	dist[0]=0;
	queue<int>que;
	que.push(0);
	while(!que.empty())
	{
		int v=que.front();
		que.pop();
		vis[v]=0;
		if(++cnt[v]>=25)return 0;
		for(int i=0;i<vec[v].size();i++)
		{
			edge temp=vec[v][i];
			if(dist[temp.to]<dist[v]+temp.cost)
			{
				dist[temp.to]=dist[v]+temp.cost;
				if(!vis[temp.to])
					vis[temp.to]=1,que.push(temp.to);
			}
		}
	}
	if(dist[24]==sum)
		return 1;
	return 0;
}
int main()
{
	read(cas);
	while(cas--)
	{
		memset(num,0,sizeof(num));
		for(int i=1;i<=24;i++)
			read(r[i]);
		read(n);
		for(int i=1;i<=n;i++)
		{
			read(k);
			num[k+1]++;
		}
		flag=0;
		for(int i=0;i<=n;i++)
		{
			BuildGraph(i);
			if(spfa(i))
			{
				flag=1;
				printf("%d\n",i);
				break;
			}
		}
		if(!flag)
			printf("No Solution\n");    
	}
	return 0;
}

poj3169

特别简单的一道题。

应该有三条公式的:

①ML:b-a<=c

②MD:b-a>=c

③题意:Si-S(i-1)>=0(可是加上这句话就错。不知道为啥).

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int n,ml,md;
const int maxn=10000000;
struct edge
{
	int to,cost;
}cur;
vector<edge>vec[1010];
inline void read(int &m)//int
{
	int x=0,f=1;char ch=getchar();//int
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	m=x*f;
}
void spfa()
{
	bool vis[1010];
	int cnt[1010],dist[1010];
	memset(vis,0,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
	fill(dist,dist+1010,maxn);
	dist[1]=0;
	queue<int>que;
	que.push(1);
	while(!que.empty())
	{
		int v=que.front();
		que.pop();
		vis[v]=0;
		if(++cnt[v]>=n)
		{
			printf("-1\n");
			return;
		}
		for(int i=0;i<vec[v].size();i++)
		{
			edge temp=vec[v][i];
			if(dist[temp.to]>dist[v]+temp.cost)
			{
				dist[temp.to]=dist[v]+temp.cost;
				if(!vis[temp.to])
					vis[temp.to]=1,que.push(temp.to);
			}
		}
	}
	if(dist[n]==maxn)
		printf("-2\n");
	else
		printf("%d\n",dist[n]);
}
int main()
{
	read(n),read(ml),read(md);
	int a,b,c;
	while(ml--)
	{
		read(a),read(b),read(c);
		cur.to=b;
		cur.cost=c;
		vec[a].push_back(cur);
	}
	while(md--)
	{
		read(a),read(b),read(c);
		cur.to=a;
		cur.cost=-c;
		vec[b].push_back(cur);
	}
	/*
	for(int i=1;i<=n;i++)
	{
		cur.to=i-1;
		cur.cost=0;
		vec[i].push_back(cur);
	}
	*/
	spfa();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值