去看看我的阶段性总结,就会发现这些东西都是n年前学的 传送门。
敲了n道题,终于对差分约束系统有了一些门道,发现他也没有我想象的那么难(虽然还是没有搞懂他与差分到底有什么关系)。
好吧,其实如同前几篇的感想一般,也有可能比较水
最近做题多了,发现差分约束都一个套路与图论spfa的关系也是分不开的
queue <int> q;//定义一个名为q的优先队列
q.push(x);//将x放入队尾
q.back();//访问队列中的最后一个元素
q.pop();//删除队列中的
q.front();//方位队列中的第一个元素
q.empty();//差点把他忘了,判断队列是否为空
有了这么一个小工具,就不用去捣鼓什么head,tail指针了,灰常方便,spfa与优先队列更配呦。
下面我们再一次回归正题
差分约束的题,有一个十分重要的东西,就是超级源点,还要注意超级远点的边与权值是根据题目的改变而改变的,而且起搭建的顺序(顺序,倒序)对时间复杂度也是有一定影响的(我也不知道为什么),据我所知,好像是倒序快那么一点点,因为我记得luogu上有一道题就是会专门卡掉你顺序的建边的。
明确了这一点之后,再来讲述一波差分约束的基本形式,在差分约束的题目中会有n个形似a-b>c的不等式,就是改变一下不等号的方向与加不加等号的问题,而我们的工作就是吧这n个不等式化为同一种形式(不加等号的那种)等号就把不等号右边的书-1(+1)然后去掉等号(因为问题都是在整数范围内的),然后具体问题具体分析,比如说求最大的问题就转化为spfa求最大生成树,最短路问题就转化为spfa求最小生成树(然而笔者并没有试过其他算法行不行),还有就是求两点之间的最大最小距离的,那么只要不把最后的dis数组累加就行了,(附加练习题【poj 3169 layout】笔者太懒,懒得贴链接(其实就是找不到了),前面的超链接是他自己的加的,和我没关系)。额,好像扯远了。顺便提一句,储存要用邻接表(废话,都说了要用spfa)
而求出了最短路之后问题一般就迎刃而解了,所以差分约束的题一般就一眼可以看出来。
什么,你说要题目的标程??
好吧好吧(请忽略奇奇怪怪的注释(笔者比较懒,懒得删))
#include <iostream>
#include <cstdio>
#include <queue>
#include <stdlib.h>
#include <memory.h>
//#pragma GCC optimize(2)
using namespace std;
#define C getchar()
#define maxn 1010
inline int read()
{
int x=0;char ch;bool flag=true;
for(;!isdigit(ch);ch=C)
if(ch=='-') flag=false;
for(;isdigit(ch);ch=C)
x=(x<<3)+(x<<1)+(ch^48);
return flag?x:-x;
}
struct biao
{
int next,to,value;
}e[20010]={};
int linkk[maxn]={};
int len;
bool vis[maxn]={};
int n,x,y;
void insert(int x,int y,int value)
{
e[++len].to=y;
e[len].value=value;
e[len].next=linkk[x];
linkk[x]=len;
}
//void rec(int x)
//{
// for(int i=1;i<=n;i++)
// {
// if(dis[x][i]==1&&!vis[i])
// {
// vis[i]=1;
// rec(i);
// }
// }
//}
queue <int> q;
int que[maxn]={};
int dist[maxn]={};
void spfa(int i)
{
memset(vis,0,sizeof(vis));
memset(que,0,sizeof(que));
memset(dist,10,sizeof(dist));//难道宏定义又翻车了??
vis[i]=1;
dist[i]=0;
que[i]++;
q.push(i);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
que[u]++;
for(int i=linkk[u];i;i=e[i].next)
{
int t=e[i].to;
if(dist[t]>dist[u]+e[i].value)
{
dist[t]=dist[u]+e[i].value;
que[t]++;
if(que[u]==n)
{
printf("%d",-1);
exit(0);
}
// cout<<"haha"<<' '<<e[i].value<<endl;
if(!vis[t]) q.push(t);
}}
}
}
int main()
{
// freopen("layout.in","r",stdin);
// freopen("layout.out","w",stdout);
n=read();
x=read();
y=read();
for(int i=1;i<=x;i++)
{
int x1=read(),y1=read(),value=read();
insert(x1,y1,value);
}
for(int i=1;i<=y;i++)
{
int x1=read(),y1=read(),value=read();
insert(y1,x1,-value);
}
// rec(1);
// if(vis[n]==0)
// {
// printf("%d",-2);
// return 0;
// }
for(int i=1;i<=n;i++)//记得luogu上的有个题目就卡这个
insert(0,i,0);// 感觉所有的差分约束题都同一个套路
// memset(vis,0,sizeof(vis));
// int ans=0;
spfa(0);
spfa(1);
if(dist[n]==168430090)
printf("%d",-2);
else
printf("%d",dist[n]);
// printf("%d",ans);
return 0;
}//万年审题不清的玩家就是我
题目大意
有好多只奶牛(竟然不是farmer john的)提出了好多个要求,好基友们的距离不可以超过n,而坏基友的距离不可以小于n,求1与n号奶牛最远可以是多少(无解出-1,无限出-2)
值得一提的是在这题的另一种做法上spfa不是void函数而是bool函数,非常巧妙地判断了-1的情况(然而笔者这么做就TLE了)
那么既然已经讲完了基本性质,就来分析一下这篇具体代码吧,在这一片代码中(第一个注释是o2优化,比赛的时候不要用,不然会和爆炸oj一个下场),首先把涞流们的奇奇怪怪的要求通过数学方法转换成a-b>c的形式,再通过邻接表储存就出现了一个图,
再通过炒鸡源点将图变为一个连通图,然后就可以通过spaf解决这个问题了
不过好像很多差分约束的题都口以用带权值的并查集做,比如说爆炸oj1202:狡猾的商人
(逃……
转载请标明出处(我相信不会有人转载的)