1. 程式人生 > >2018.12.25-dtoj-4086-針老師(truth) || dtoj-3657: 排列(permutation)

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\n",ans-dinic()); return 0; }
View Code