2018.12.25-dtoj-4086-针老师(truth) || dtoj-3657: 排列(permutation)

题目描述:

为了证明自己是 OI 界最针的人,针老师打算向全 LOJ 群最针的 wxh 发起挑战。针老师偷偷潜入 wxh 所在地的电力设施,打算一举剿灭 wxh。供电的网络是由 n 个节点组成的 DAG,每个节点有一定的 能量 (能量可能为负)。针老师仔细研修后发现,为了打败 wxh,他必须选择一个该 DAG 的拓扑序,然后 将拓扑序上连续一段的能量节点摧毁。为了制定合理的作战计划,针老师邀请你来计算最大能摧毁的能量之和。 

算法标签:网络流

思路:

怎么想到网络流啊天啊神仙!

解释一下建图,拓扑序相当于得到条件a连向b,说明a在序列中必然在b之前。于是我们把序列分成选中序列,选中序列前,及选中序列后三个部分。将每一个权值为正的数与s和t,分别连一条边,边权为点权,表示把这个点从中间移出,放到序列前或后。对于点权为负的点,把自己与s和t相连两个点连接,边权为自己点权的相反数,割掉表示把他放进中间序列。然后对于每一个限制条件,如a在b前,连接a到b,及a+n到b+n,边权是inf

以下代码:

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=10e3+5,M=8e3+5,inf=1e9;
int n,m,head[N],ne[M],to[M],cnt=1,v[M],s,t,d[N],ans;queue<int> q;
il int read(){int x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return f*x;}
il void insert(int x,int y,int z){ne[++cnt]=head[x];head[x]=cnt;to[cnt]=y;v[cnt]=z;}
il void add(int x,int y,int z){insert(x,y,z);insert(y,x,0);}
il bool bfs(){
    for(int i=1;i<=t;i++)d[i]=-1;q.push(s);d[s]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=ne[i]){
            if(d[to[i]]!=-1||(!v[i]))continue;
            d[to[i]]=d[x]+1;q.push(to[i]);
        }
    }
    return d[t]!=-1;
}
il int dfs(int x,int f){
    if(x==t)return f;int used=0,w;
    for(int i=head[x];i;i=ne[i]){
        if(d[to[i]]!=d[x]+1||(!v[i]))continue;
        w=min(v[i],f-used);w=dfs(to[i],w);
        used+=w;v[i]-=w;v[i^1]+=w;if(used==f)return used;
    }
    if(!used)d[x]=-1;return used;
}
il int dinic(){int res=0;while(bfs())res+=dfs(s,inf);return res;}
int main()
{
    n=read();m=read();s=0;t=n<<1|1;
    for(int i=1;i<=n;i++){
        int x=read();
        if(x>0)add(s,i,x),add(i+n,t,x),ans+=x;
        else add(i,i+n,-x);
    }
    for(int i=1;i<=m;i++){
        int x=read(),y=read();add(x,y,inf);add(x+n,y+n,inf);
    }
    printf("%d
",ans-dinic());
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Jessie-/p/10176436.html