您的位置:首页 > 编程语言 > C语言/C++

差分约束系统详解 BZOJ 2330 糖果

2016-06-07 23:01 351 查看

差分约束系统

有一个男人他是这样说的

我们有如下几个式子:

A-B<=x —-> A最多比B大x

A-C<=y —-> A最多比C大y

B-C<=z —-> B最多比C大z

所有的约束条件反映了一个问题:

一个数不可能过大,因为某个数可能至多比某个数大k,所以这是一类有最大值问题。

那么假如对于A-B<=x ,我们由B向A连一条大小为x的边。

对于所有等式,由C向B连一条z的边,B向A连一条x的边,C向A连一条y的边。

我们看一下,C到A的路径有两条,一条长度为y的代表A至多比C大y。

另一条路径C–>B–>A的长度为x+z,代表A至多比C大x+z。

最后我们发现C到A的最短路径就是C至多比A大多少,即有最大值限制。

跑SPFA求最短路,记得判断负权回路。

可是另一个男人他是这样说的

A-B>=x —-> A至少比B大x

A-C>=0 —-> A至少和C相等

B-C>=0,C-B>=0 —-> B的值等于C的值

所有的约束条件也反映了一个问题:

一个数不可能过小,因为某个数至少要比某个数大,所以这是一类有最小值问题。

我们对于A-B>=x,我们由B向A连一条大小为x的边。

我们从B走向A的最长(若有正环代表循环约束约束失败)路径就是A至少比B大多少。

我们跑SPFA最长路径,记得判断正环。

对于BZOJ2330我们发现它是第二类问题

我们首先创造出一个0号节点指向每个节点大小为1代表每个小朋友至少分一个。

然后五个式子都可以化简成A-B>=X或B-A>=X,然后跑一下即可。

这题不用int会超,所以注意这句话:typedef int ll;开始其实代的long long……

还有0向n个小盆友的边要从大(n)到小(1)连,否则超时,大爷告诉我的……

@PoPoQQQ,还好告诉我了要不得T一晚上!?F:>:@??>!:”>LE:>@!!

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<queue>
#include<vector>
#include<climits>
#include<string>
#include<cstdlib>
#include<set>
#include<stack>
#include<map>
#include<bitset>
#include<ctime>
using namespace std;
typedef int ll;
typedef unsigned long long ull;
inline ll read()
{
char k=0;char ls;ls=getchar();for(;ls<'0'||ls>'9';k=ls,ls=getchar());
ll x=0;for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
if(k=='-')x=0-x;return x;
}
struct E{
ll next;
ll zhi;
ll s;
}e[400500];
ll last[100500];
ll n,m,w;
ll dis[100500];
ll vis[100500];
ll pan[100500];
long long ans;
queue<ll>q;

bool spfa()
{
q.push(0);
++vis[0];
pan[0]=1;
while(!q.empty())
{
ll d=q.front();
q.pop();
pan[d]=0;
for(int j=last[d];j;j=e[j].zhi)
{
ll z=e[j].next;
if(dis[z]<dis[d]+e[j].s)
{
dis[z]=dis[d]+e[j].s;
++vis[z];
if(vis[z]>(n+1))
return 0;
if(pan[z]==0)
{
q.push(z);
pan[z]=1;
}
}
}
}
return 1;
}

void Add(ll a,ll b,ll s)
{
e[++w].next=b;
e[w].s=s;
e[w].zhi=last[a];
last[a]=w;
return;
}

int main()
{
n=read(),m=read();
for(int i=1;i<=m;++i)
{
ll x=read(),a=read(),b=read();
switch(x)
{
case 1:
Add(a,b,0);
Add(b,a,0);
break;
case 4:
swap(a,b);
case 2:
Add(a,b,1);
break;
case 3:
swap(a,b);
case 5:
Add(a,b,0);
break;
}
}
for(int i=n;i>=1;--i)
Add(0,i,1);

if(!spfa())
cout<<-1<<endl;
else
{
for(int i=1;i<=n;++i)
{ans+=dis[i];}
cout<<ans<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言