差分约束总结

  遇到了一些很不错的差分约束系统的题目 我觉得需要总结一下的样子。

LINK:种树 万壑树参天 千山响杜鹃  差分约束 觉得海星 由于每个位置只能种一棵树和区间的限定我们不难想到 利用前缀和数组来建图。

显然 有条件 dr-dr-1<=1 dr-dr-1>=0 和某些去加问题 dr-dl-1>=cnt 什么 的通过建图 我们发现要构造出来的解其实就是 d数组了。

考虑 一下怎么跑?从哪开始跑 好像没有一个起点的样子 虚设源点 从而进行构解 源点如果为 0的话 好像没有什么约束条件的样子但是源点设为 n+1 那么则有 dn+1-di>=0

这个条件 我们设dn+1=0 构造出来一组负数解 即可 考虑如何满足最小的取值 我觉的是 由于跑的是最短路 所以求出的解必然是一组 可能不符合实际意义但是是最优的解 。

所以我们调整一下即可 全部都减去最小的 最小的显然是dis[1] 由于dis n+1为0 答案就为 disn+1-dis[1] ==-dis[1]了。(我觉得应该没锅

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cctype>
#include<cstdlib>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#define INF 1000000010
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define EPS 1e-5
#define l(p) t[p].l
#define r(p) t[p].r
#define sum(p) t[p].sum
#define zz p<<1
#define yy p<<1|1
#define mod 1000000007
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=30010;
int n,flag,ans;
int m,len,t,h,minn=INF;
int dis[MAXN],vis[MAXN],v[MAXN],q[MAXN<<5];
int lin[MAXN],nex[MAXN<<2],ver[MAXN<<2],e[MAXN<<2];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void spfa()
{
    while(h++<t)
    {
        int x=q[h];vis[x]=0;
        for(int i=lin[x];i;i=nex[i])
        {
            int tn=ver[i];
            if(dis[tn]>dis[x]+e[i])
            {
                v[tn]=v[x]+1;
                if(v[tn]>=n){flag=1;return;}
                dis[tn]=dis[x]+e[i];
                if(!vis[tn])q[++t]=tn,vis[tn]=1;
            }
        }
    }
}
signed main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();++n;
    memset(dis,0x3f,sizeof(dis));
    for(int i=1;i<n;++i)
    {
        add(i,i+1,1),add(i+1,i,0);
        add(n,i,0);
    }
    for(int i=1;i<=m;++i)
    {
        int x,y,z;
        x=read();y=read()+1;z=read();
        add(y,x,-z);
    }
    q[++t]=n;dis[n]=0;
    spfa();
    printf("%d\n",-dis[1]);
    return 0;
}
View Code

LINK:狡猾的商人 商人 总是 具有高雅的智慧吧。

还是考虑前缀和建图 不过不同的是 某个区间特定的值是有了我们是这样转换的 dr-dl-1>=w dr-dl-1<=w

所以就可以直接跑了 ...

//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define INF 1000000000
#define ll long long
#define db double
#define mod 1000000007
#define pii pair<ll,ll>
#define mk make_pair
#define l(p) t[p].l
#define r(p) t[p].r
#define sum(p) t[p].sum
#define zz p<<1
#define yy p<<1|1
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=110,maxn=1010;
int T,len,t,h;
int n,m,flag;
int lin[MAXN],nex[maxn<<1],ver[maxn<<1],e[maxn<<1];
int vis[MAXN],dis[MAXN],v[MAXN],q[MAXN*MAXN];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void spfa()
{
    while(h++<t)
    {
        int x=q[h];vis[x]=0;
        for(int i=lin[x];i;i=nex[i])
        {
            int tn=ver[i];
            if(dis[x]+e[i]<dis[tn])
            {
                dis[tn]=dis[x]+e[i];
                v[tn]=v[x]+1;
                if(v[tn]>=n){flag=1;return;}
                if(!vis[tn])q[++t]=tn,vis[tn]=1;
            }
        }
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    T=read();
    while(T--)
    {
        flag=0;len=0;
        memset(lin,0,sizeof(lin));
        n=read();m=read();++n;
        for(int i=1;i<=m;++i)
        {
            int l,r,z;
            l=read();r=read()+1;z=read();
            add(l,r,z);
            add(r,l,-z);
        }
        t=h=0;
        for(int i=1;i<=n;++i)
        {
            q[++t]=i;
            v[i]=dis[i]=vis[i]=0;
        }
        spfa();
        if(flag)puts("false");
        else puts("true");
    }
    return 0;
}
View Code

LINK:天平 其实在我的眼中天平从来都没有平过..毫无用处的东西...

这题就很神了 很鬼畜的样子 发现差分约束总是 要对建图有一定的要求。

有一堆约束条件 但是可能有未知的砝码 所以最后我们利用spfa构造的只是其中的一种很难完成题目中所说的 确定的情况。

神仙题目 原来跑的是floyd 处理差分约束的关系。可以设两个数字 d[i][j] 表示i和j砝码最多差多少克 b i j 表示i和j砝码最少差多少克。

这个东西可以利用floyd实现 这个东西 然后最后求的话其实就是暴力枚举两个东西 判断即可。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cctype>
#include<cstdlib>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#define INF 100000000000000000ll
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define EPS 1e-5
#define mod 1000000007
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=60;
int n;
int A,B,ans1,ans2,ans3;
int d[MAXN][MAXN],b[MAXN][MAXN];
char a[MAXN][MAXN];
int main()
{
    //freopen("1.in","r",stdin);
    n=read();A=read();B=read();
    for(int i=1;i<=n;++i)
    {
        scanf("%s",a[i]+1);
        for(int j=1;j<=n;++j)
        {
            if(a[i][j]=='?')d[i][j]=2,b[i][j]=-2;
            if(a[i][j]=='+')d[i][j]=2,b[i][j]=1;
            if(a[i][j]=='-')d[i][j]=-1,b[i][j]=-2;
            if(a[i][j]=='=')d[i][j]=b[i][j]=0;
        }
    }
    for(int k=1;k<=n;++k)
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
            {
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
                b[i][j]=max(b[i][j],b[i][k]+b[k][j]);
            }
    for(int i=1;i<=n;++i)
    {
        if(i==A||i==B)continue;
        for(int j=i+1;j<=n;++j)
        {
            if(j==A||j==B)continue;
            if(b[A][i]>d[j][B]||b[B][i]>d[j][A])++ans1;
            if(b[i][A]>d[B][j]||b[j][A]>d[B][i])++ans3;
            if((b[A][i]==d[A][i]&&b[j][B]==d[j][B]&&b[A][i]==b[j][B])||(b[A][j]==d[A][j]&&b[i][B]==d[i][B]&&b[A][j]==b[i][B]))++ans2;
        }
    }
    printf("%d %d %d\n",ans1,ans2,ans3);
    return 0;
}
View Code

 

posted @ 2019-10-15 10:38  chdy  阅读(190)  评论(0编辑  收藏  举报