差分约束zoj2770

题目链接

题意大概就是给我们 n n n个营地, c [ i ] c[i] c[i]表示第 i i i个营地最多的人数,第 u u u-第 v v v个营地一共有 w w w个人。问最少有多少人。
对于这种题型,我们一般做法是首先确定一个数组表示前面 i i i个营地的总人数

挖掘隐藏条件,每一个营地的人数不可能为负数,所以 a [ i ] − a [ i − 1 ] ≥ 0 a[i]-a[i-1]\geq0 a[i]a[i1]0
每个营地最多有 i i i人,我们可以知道, a [ i ] − a [ i − 1 ] ≤ c [ i ] a[i]-a[i-1]\leq c[i] a[i]a[i1]c[i]
u − v u-v uv一共最少有 w w w人,我们可以得出, a [ u ] − a [ v − 1 ] ≥ w a[u]-a[v-1]\geq w a[u]a[v1]w

我们可以把第二个条件化简为
a [ i ] − a [ i − 1 ] ≤ a [ i ] → a [ i − 1 ] − a [ i ] ≥ − c [ i ] a[i]-a[i-1]\leq a[i] \rightarrow a[i-1]-a[i] \geq -c[i] a[i]a[i1]a[i]a[i1]a[i]c[i]
由上化简,我们就把所有的代数式换成了大于等于的形式,开始建图了
题目要求的是最小的人数,那么也就是 m i n ( a [ n ] − a [ 0 ] ) min(a[n]-a[0]) min(a[n]a[0])那么我们就可以这么建图:

我们按照从被减数向减数建一条边,边权为右边的值。

这样建图有什么好处?因为我们要求的是 m i n ( a [ n ] − a [ 0 ] ) min(a[n]-a[0]) min(a[n]a[0]),而我们这么建图就会使源点 0 0 0到达的任意一个 v v v最长路长度都是最少的人数,为什么是最长路?大于等于符号决定的呀,当大于等于时,只有最长路不会和其他方向的边冲突,小于等于时,我们只有最短路不会和其他方式的边冲突
接下来看代码,spfa版本

#include<stdio.h>
#include<algorithm>
#include<string.h>
//#include<windows.h>
using namespace std;
const int N = 2e5+5;
int deq[N*2],a[N],head[N],tot=0;
struct ED
{
    int pre,to,w;
}ed[2*N];
int vis[N],dis[N],num[N];
void spfa(int x){
    memset(dis,-1,sizeof dis);
    memset(vis,0,sizeof vis);
    memset(deq,0,sizeof deq);
    memset(num,0,sizeof num);
    dis[0]=0;
    int l=1,r=0;
    deq[++r]=0;
    while(l<=r){
        int u=deq[l];
        l++;
        vis[u]=0;
        //printf("%dsss\n",u);
        for(int i=head[u];~i;i=ed[i].pre){
            int v=ed[i].to;
            //printf("%d %d %d %d %d\n",u,v,dis[u],ed[i].w,dis[v]);
            if(dis[v]<dis[u]+ed[i].w) {
                dis[v]=dis[u]+ed[i].w;
                if(vis[v]) continue;
                deq[++r]=v;
                num[v]++;
                if(num[v]>=x) {
                    printf("Bad Estimations\n");
                    return ;
                }
                vis[v]=1;
                //printf("%d %d\n",u,v);
            }
        }
    }
    if(dis[x]==-1){
        printf("Bad Estimations\n");
    }
    else {
        printf("%d\n",dis[x]);
    }
}
void add(int u,int v,int w){
    ed[++tot].pre=head[u];
    ed[tot].to=v;
    ed[tot].w=w;
    head[u]=tot;
}
int main(){
    int n,m,i,j,k;
    while (scanf("%d %d",&n,&m)==2)
    {
        tot=0;
        for(i=0;i<=n;i++){
            head[i]=-1;
        }
        for(i=1;i<=n;i++){
            scanf("%d",&a[i]);
            add(i-1,i,0);
            add(i,i-1,-a[i]);
        }
        int u,v,w;
        for(i=1;i<=m;i++){
            scanf("%d %d %d",&u,&v,&w);
            add(u-1,v,w);
        }
        spfa(n);
    }
    //system("pause");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值