差分约束系统【USACO DEC05 GOLD】设计

最近几天考试这个知识点出现挺多次啊=-=(也就两次哪里多了)

还好没有省选组哲♂学家们的 一天一个LCT恐怖

于是心血来潮弄了一下这个叫 差分约束系统 的知识点......诶 不就是最短路吗啊啊啊啊 真是嗑了一桶枣药丸了

必备知识 : 了解最短路求法和其中的本质——三角不等式 不懂的戳下面的传送门

先安利一波十分详细的 讲差分约束系统博客 http://www.cppblog.com/menjitianya/archive/2015/11/19/212292.html

此处只讲题目 和分析~

题目:

Description

  和人一样,牛也喜欢站得离朋友较近的位置。FJ有N(2<=N<=1,000)头牛,编号为1..N,现在要设计一个顺序让他们站成一排给他们喂食。奶牛们按照编号顺序依次站立,允许有多只牛站在同一位置(也就是说,牛i和牛j(i<j)的站立位置s_i,s_j一定满足s_i<=s_j,如果s_i=s_j,那么编号为i到j之间的牛也一定站在s_i处)。
  有一些牛相互喜欢,希望两人的距离在某个范围内,同样也有一些牛相互不喜欢,希望两人的距离大于等于某个距离,题目中给出ML(1<=ML<=10,000)个限制描述相互喜欢的情况,给出MD(1<=MD<=10,000)个限制描述相互不喜欢的情况。
  你的任务是计算,如果存在某种方案满足上述要求,输出1号牛和N号牛之间最大距离。

Input

  第1行:3个空格隔开的整数N,ML,MD。
  第2到ML+1行:每行包含3个空格隔开的整数A,B和D,满足1<=A<B<=N,表示牛A和牛B之间的距离不得超过D(1<=D<=1,000,000)。
  第ML+2到ML+MD+1行:每行包含3个空格隔开的整数A,B和D,满足1<=A<B<=N,表示牛A和牛B之间的距离至少为D(1<=D<=1,000,000)。

Output

  如果不存在这样的方案,输出-1,如果牛1和牛N之间的距离可以任意,输出-2,否则输出最大的距离。

Sample Input

4 2 1
1 3 10
2 4 20
2 3 3

Sample Output

27

Hint

【样例说明】
  最佳方案是1到4号牛依次放置于位置0,7,10,27


很明显的差分约束系统=-=(虽然考试时我知道大概是什么东西可是没学就是写不出来)

因为A<B 输入的 u 必定小于 v (这个..就是变量啦 看程序就知道了)

我们把两种式子先列出来——

        喜欢: 从 u 到 v 小于等于 w 即 v - u ≤ w

        不喜欢: 从 u 到 v 大于等于 w 即 v - u ≥ w

好了 现在怎么处理呢

个人认为 因为要满足的约束条件绝对是比较小那个 那我们更新的时候要取最小的价值 则

将不喜欢的式子略作改动 取相反数 得 u - v ≤ -w

看起来怪怪的 权值为负?? 没错 但因为什么blab不变定理(某式两边同时取反改符号满足某式条件)

说通俗点就是 本题 u 绝对小于等于 v 则 u - v ≤0 等式两边都不是正数 这样想想就正常了吧

然后邻接表存修改过的边 SPFA一下就好(开始我还在怕判负环复杂度接近O(mn) 结果3ms过了...)

下放代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX = 1 << 10;
struct Edge {
	int next,to,v;
}e[20010];
int first[MAX],dis[MAX],vis[MAX],pd[MAX],pre[MAX << 13],h,t;
void add(int x,int y,int z)
{
	e[++t].to = y;
	e[t].next = first[x];
	e[t].v = z;
	first[x] = t;
}
void reset()
{
	memset(dis,0x7f,sizeof(dis));
	dis[1] = 0;
	pre[1] = 1;
	vis[1] = 1;
	t = 1;
}
int main()
{
	int n,like,disl;
	scanf("%d%d%d",&n,&like,&disl);
	int u,v,w;
	for (int a=1;a<=like;++a) scanf("%d%d%d",&u,&v,&w),add(u,v,w);//存like的边
	for (int a=1;a<=disl;++a) scanf("%d%d%d",&u,&v,&w),add(v,u,-w);//存修改后的dislike的边
	reset();//为了在水题上显得更加高大上 初始化ban♂到子程序里
	while (h < t)//SPFA
	{
		int p = pre[++h];
		pd[p] = 0;
		for (int a = first[p],b = e[a].to ; a ; a = e[a].next,b = e[a].to)//典型的SPFA 不想注释=-=
			if (dis[b] > dis[p] + e[a].v)
			{
				dis[b] = dis[p] + e[a].v;
				if (++vis[b] >= n) {printf("-1\n"); return 0;}//判断负环:如果一个点被更新n次 就说明有负环
				if (!pd[b]) pd[b] = 1,pre[++t] = b;
			}
	}
	if (dis[n] == dis[0]) printf("-2\n"); else printf("%d\n",dis[n]);//如果dis[n]的数值没被改变 则图不连通 其间可以隔无限距离 输出-2
	return 0;
}

这题就这样啦 这类题目注意存边的推导就好......

猜你喜欢

转载自blog.csdn.net/Frocean/article/details/81017349
今日推荐