zoukankan      html  css  js  c++  java
  • luogu P3980 [NOI2008]志愿者招募

    传送门

    网络流又一神仙套路应用

    首先考虑列不等式,设(x_i)为第i种人的个数,记(b_{i,j})为第i种人第j天是否能工作,那么可以列出n个不等式,第j个为(sum_{i=1}^{m}b_{i,j}x_ige a_j)

    然后将这些不等式转成等式,新开变量y,则那些不等式可以写为$$egin{cases}b_{1,1}x_1+b_{2,1}x_2...+b_{m,1}x_m=a_1+y_1_{1,2}x_1+b_{2,2}x_2...+b_{m,2}x_m=a_2+y_2......end{cases}$$

    然后每个式子都减去上面的式子(假装最后有一个(0=0)的式子),然后得到(n+1)个等式.每个等式再移项,使得系数全为正数.我们发现每个变量分别在某个等式左边出现一次,也在某个右边出现一次.所以可以联系网络流是流入流量=流出流量的,这里把每个等式看做一个点,左边看做流入,右边看做流出.对于变量(x_i),从流出的点向流入的点连流量为能用的最大次数(本题为Inf),费用为单个代价的边;对于变量(y_i),从流出的点向流入的点连流量为能用的最大次数(本题为Inf),费用为0的边;对于常数项(a_i),从原点向流入点连流量(a_i)费用0边,从流出点向汇点连流量(a_i)费用0边,然后费用流求费用就好了

    感性理解一下?(

    #include<bits/stdc++.h>
    #define LL long long
    #define il inline
    #define re register
    #define db double
    
    using namespace std;
    const int N=1e4+10,M=1e5+10;
    il int rd()
    {
        int x=0,w=1;char ch=0;
        while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
        return x*w;
    }
    int n,m;
    int a[N],b[N][3];
    int ps,pt,to[M],nt[M],c[M],hd[N],tot=1;
    LL w[M];
    il void add(int x,int y,int z,int zz)
    {
        ++tot,to[tot]=y,nt[tot]=hd[x],c[tot]=z,w[tot]= zz,hd[x]=tot;
        ++tot,to[tot]=x,nt[tot]=hd[y],c[tot]=0,w[tot]=-zz,hd[y]=tot;
    }
    LL di[N],ans;
    int pre[N],fw[N];
    bool v[N];
    queue<int> q;
    il bool csfl()
    {
        memset(di,0x3f3f3f,sizeof(di));
        memset(fw,0,sizeof(fw));
        di[ps]=0,fw[ps]=1<<30,v[ps]=1,q.push(ps);
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            for(int i=hd[x];i;i=nt[i])
            {
                int y=to[i];
                if(c[i]>0&&di[y]>di[x]+w[i])
                {
                    di[y]=di[x]+w[i];
                    pre[y]=i,fw[y]=min(fw[x],c[i]);
                    if(!v[y]) v[y]=1,q.push(y);
                }
            }
            v[x]=0;
        }
        if(di[pt]==di[pt+1]) return 0;
        ans+=1ll*di[pt]*fw[pt];
        int x=pt;
        while(x^ps)
        {
            int i=pre[x];
            c[i]-=fw[pt],c[i^1]+=fw[pt];
            x=to[i^1];
        }
        return 1;
    }
    
    int main()
    {
        n=rd(),m=rd();
        for(int i=1;i<=n;++i) a[i]=rd();
        for(int i=1;i<=m;++i) b[i][0]=rd(),b[i][1]=rd(),b[i][2]=rd();
        ps=0,pt=n+3;
        for(int i=1;i<=m;++i) add(b[i][1]+1,b[i][0],1<<30,b[i][2]);
        for(int i=1;i<=n;++i) add(i,i+1,1<<30,0);
        for(int i=1;i<=n;++i) add(ps,i+1,a[i],0),add(i,pt,a[i],0);
        while(csfl());
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    Java实现 LeetCode 27 移除元素
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 24 两两交换链表中的节点
    Java实现 LeetCode 24 两两交换链表中的节点
    Java实现 LeetCode 24 两两交换链表中的节点
  • 原文地址:https://www.cnblogs.com/smyjr/p/10269427.html
Copyright © 2011-2022 走看看