POJ3169 差分约束系统 解题报告

【问题描述】  
  
  如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi≤bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。 


  下面是关于差分约束系统一个实际问题的描述: 


  FJ有 n 头奶牛(编号从 1 到 n),沿一条直线站着等候喂食,由于奶牛们身材都比较苗条,所以可能有多头奶牛站在同一个位置的情况。设第i头奶牛站队位置为d[i],在安排每头奶牛站队位置d[i]时需要满足如下的条件:
  1、奶牛应按照编号顺序站队,即d[i]-d[i+1]<=0。
  2、m对友好的牛希望彼此站的近些。即对于友好关系(i,j,D)有d[j]-d[i]≤D(1 ≤ i < j ≤ n)。
  3、p对敌对的牛希望彼此站的远些。即对于敌对关系(i,j,L)有d[j]-d[i]≥L(1 ≤ i < j ≤ n)。


  现在请你帮助FJ判断是否存在满足这些条件的一种站队方案,如果存在请计算1号奶牛与n号奶牛之间的最大距离。如果不存在满足要求的方案,输出-1;如果1号奶牛和N号奶牛间的距离可以任意大,输出-2; 
 
    
 【输入格式】  
  
  第 1 行输入 n,m,p,接下来的 m 行,每行三个整数 i,j,D,表示牛 i 和牛 j 的距离最多为 D,再接下来的 p 行,每行三个整数 i,j,L,表示牛 i 和牛 j 的距离最少为 L。
 
    
 【输出格式】  
   
  如果不存在满足要求的方案,输出-1;如果1号奶牛和N号奶牛间的距离可以任意大,输出-2;否则,计算出在满足所有要求的情况下,1 号奶牛和 N 号奶牛间可能的最大距离。
 
    
 【输入样例】   
   
4 2 1
1 3 10
2 4 20
2 3 3
 
    
 【输出样例】  
   
27
 
    
 【样例解释】  
   
四只奶牛的位置分别为:0,7,10,27
 
    
 【数据范围】  
   
2≤n≤1000  ,  1≤m,p≤10000  , 1≤D,L≤1000000
 
    
 【来源】  
  
poj3169


 解题思路:根据题意,求差分约束系统的方法,就是将所给的信息,转化为图,通过求最优路径达到目的。那么,怎么将输入的信息有效地转化为图呢(重点,难点)?看到这么多个不等式,不难想到求最短路径时的三角图的不等式——d[j]<=d[i]+c,将不等式变一下形可以得到d[j]-d[i]<=c,此时从i出发连了一条有向边到j,同理,将题中所给的不等式变形成与上式一样的形式,可以得到d[i]-d[i+1]<=0,d[j]-d[i]<=D,d[i]-d[j]<=-L,将这些不等式与上式类比,则可以看成i+1到i连了一条边权为0的有向边,i到j连了一条边权为D的有向边,j到i连了一条边权为-L的有向边,至此,图被我们建立了起来,问题由两头奶牛间的最大距离转化为求图上两点的最短路径(这里解释一下为什么求的是最短路径,因为根据三角图关系d[j]<=d[i]+c,说明在满足条件的情况下,d[j]最大可以取到d[i]+c)。需要注意的是,这里要判断是否存在满足要求的方案及奶牛间的距离是否可以无限大,如果图中存在负权回路,则说明没有满足要求的方案,如果计算完最短路径后d[N]的值仍为初始时的无穷大,则说明奶牛间的距离可以无限大。


#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn=1005;
const int inf=1000000010;
int n,m,p,a,b,D,L;
vector<int>g[maxn],w[maxn];
int d[maxn],inq[maxn],num[maxn];
int SPFA(int s)  //SPFA算法计算最短路径并判断是否有负权回路
{
	queue<int>q;
	for(int i=1;i<=n;i++)
	d[i]=inf;
	memset(inq,0,sizeof(inq));
	memset(num,0,sizeof(num));
	q.push(s);
	inq[s]=1;
	num[s]++;
	d[s]=0;
	while(!q.empty())
	{
		int i=q.front();  q.pop();
		inq[i]=0;
		for(int k=0;k<g[i].size();k++)
		{
			int j=g[i][k],c=w[i][k];
			if(d[i]+c<d[j])
			{
				d[j]=d[i]+c;
				num[j]++;
				if(num[j]==n)  
				{
					return 0;  //存在负权回路
				}
				if(inq[j]==0)
				{
					q.push(j);
					inq[j]=1;
				}
			}
		}
	}
	return 1;
}
int main()
{	
	freopen("48.in","r",stdin);
	//freopen("48.out","w",stdout);
	scanf("%d%d%d",&n,&m,&p);
	for(int i=1;i<n;i++)   //d[i]-d[i+1]<=0
	{
		g[i+1].push_back(i);
		w[i+1].push_back(0);
	}
	for(int i=1;i<=m;i++)  //d[b]-d[a]<=D
	{
		scanf("%d%d%d",&a,&b,&D);
		g[a].push_back(b);
		w[a].push_back(D);
	}
	for(int i=1;i<=p;i++)  //d[a]-d[b]<=-L
	{
		scanf("%d%d%d",&a,&b,&L);
		g[b].push_back(a);
		w[b].push_back(-L);
	}
	if(SPFA(1)==0)  
	{
		printf("-1\n");
	}
	if(SPFA(1)==1)  
	{
		if(d[n]==inf)  printf("-2\n");  //奶牛间的距离可以无限大
		else  printf("%d\n",d[n]);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值