题链:
http://poj.org/problem?id=2125
题解:
最小割 + 输出割方案。
建图:
拆点,每个题拆为 i 和 i'分别表示其的入点和出点
建立超源 S和超汇 T。
S -> i :(outi[i]) 割了这个边相当于选择了 i-
i'-> T :(ini[i]) 割了这个边相当于选择了 i+
u -> v':(INF)
然后求Dinic跑最大流求最小割。
至于求方案,首先显然割的边都和 S或 T相连。
从 S开始沿着非满载的边(包括反向边)dfs,对遇到的点打标记。
如果某个入点 i 没有被标记过,则输出 i -
如果某个出点 i'被标记过,则输出 i +
代码:
#include<queue> #include<cstdio> #include<cstring> #include<iostream> #define MAXN 500 #define MAXM 150000 #define INF 0x3f3f3f3f using namespace std; struct Edge{ int to[MAXM],cap[MAXM],nxt[MAXM],head[MAXN],ent; void Init(){ ent=2; memset(head,0,sizeof(head)); } void Adde(int u,int v,int w){ to[ent]=v; cap[ent]=w; nxt[ent]=head[u]; head[u]=ent++; to[ent]=u; cap[ent]=0; nxt[ent]=head[v]; head[v]=ent++; } int Next(int i,bool type){ return type?head[i]:nxt[i]; } }E; int cur[MAXN],d[MAXN],mark[MAXN]; int N,M,S,T; int idx(int i,int k){ return i+k*N; } bool bfs(){ queue<int>q; memset(d,0,sizeof(d)); d[S]=1; q.push(S); int u,v; while(!q.empty()){ u=q.front(); q.pop(); for(int i=E.Next(u,1);i;i=E.Next(i,0)){ v=E.to[i]; if(d[v]||!E.cap[i]) continue; d[v]=d[u]+1; q.push(v); } } return d[T]; } int dfs(int u,int reflow){ if(u==T||!reflow) return reflow; int flowout=0,f,v; for(int &i=cur[u];i;i=E.Next(i,0)){ v=E.to[i]; if(d[v]!=d[u]+1) continue; f=dfs(v,min(reflow,E.cap[i])); flowout+=f; E.cap[i^1]+=f; reflow-=f; E.cap[i]-=f; if(!reflow) break; } if(!flowout) d[u]=0; return flowout; } int Dinic(){ int flow=0; while(bfs()){ memcpy(cur,E.head,sizeof(E.head)); flow+=dfs(S,INF); } return flow; } void DFS(int u){ mark[u]=1; for(int i=E.Next(u,1);i;i=E.Next(i,0)){ if(!E.cap[i]||mark[E.to[i]]) continue; DFS(E.to[i]); } } int main() { while(~scanf("%d%d",&N,&M)){ memset(mark,0,sizeof(mark)); S=2*N+1; T=2*N+2; E.Init(); for(int i=1,x;i<=N;i++) scanf("%d",&x),E.Adde(idx(i,1),T,x); for(int i=1,x;i<=N;i++) scanf("%d",&x),E.Adde(S,idx(i,0),x); for(int i=1,u,v;i<=M;i++) scanf("%d%d",&u,&v),E.Adde(idx(u,0),idx(v,1),INF); int ans=Dinic(),num=0; printf("%d ",ans); DFS(S); for(int i=1;i<=N;i++) num+=((!mark[i])+(mark[i+N])); printf("%d ",num); for(int i=1;i<=N;i++) { if(!mark[i])printf("%d - ",i); if(mark[i+N])printf("%d + ",i); } } return 0; }