题意:
裸的最大权闭合图。
分析:
国家集训队论文<<最小割模型在信息学竞赛中的应用>>里有完整的建模推导证明,建议搜了学习下~这里直接用dinic搞了。
代码:
//poj 2987
//sep9
#include <iostream>
#include <queue>
using namespace std;
typedef long long INT;
const INT MAXN=5000;
const INT MAXM=60000;
const INT MAX=INT_MAX*(INT)(INT_MAX-3);
struct Edge
{ INT v,nxt,f;
}e[MAXM*2+10];
queue<INT> que;
INT src,sink;
INT g[MAXN+10];
INT nume;
bool vis[MAXN+10];
INT dist[MAXN+10]; INT n,m,vertex_cnt;void addedge(INT u,INT v,INT c)
{ e[++nume].v=v;e[nume].f=c;e[nume].nxt=g[u];g[u]=nume; e[++nume].v=u;e[nume].f=0;e[nume].nxt=g[v];g[v]=nume;
} void init()
{ memset(g,0,sizeof(g)); nume=1;
} INT bfs()
{ while(!que.empty()) que.pop(); memset(dist,0,sizeof(dist)); memset(vis,0,sizeof(vis)); vis[src]=true; que.push(src); while(!que.empty()){ INT u=que.front();que.pop(); for(INT i=g[u];i;i=e[i].nxt) if(e[i].f>0&&!vis[e[i].v]){ que.push(e[i].v); dist[e[i].v]=dist[u]+1; vis[e[i].v]=true; if(e[i].v==sink) return 1; } } return 0;
} INT dfs(INT u,INT delta)
{ if(u==sink) return delta; INT ret=0; for(INT i=g[u];ret<delta&&i;i=e[i].nxt) if(e[i].f>0&&dist[e[i].v]==dist[u]+1){ INT dd=dfs(e[i].v,min(e[i].f,delta-ret)); if(dd>0){ e[i].f-=dd; e[i^1].f+=dd; ret+=dd; } else dist[e[i].v]=-1; } return ret;
} INT dinic()
{ INT ret=0; while(bfs()==1) ret+=dfs(src,MAX); return ret;
} void solve(INT u)
{++vertex_cnt;vis[u]=true;for(INT i=g[u];i;i=e[i].nxt) if(e[i].f>0&&!vis[e[i].v])solve(e[i].v);
}int main()
{scanf("%lld%lld",&n,&m);INT W=0;init();src=0,sink=n+1; for(INT i=1;i<=n;++i){INT w;scanf("%lld",&w);if(w>0){W+=w;addedge(src,i,w);}else if(w<0){addedge(i,sink,-w);}}INT u,v;for(INT i=0;i<m;++i){scanf("%lld%lld",&u,&v);addedge(u,v,MAX);}INT max_profit = W - dinic();vertex_cnt=0;memset(vis,0,sizeof(vis));solve(src);printf("%lld %lld\n",vertex_cnt-1,max_profit); return 0;
}