题意:
裸的最大权闭合图。
分析:
国家集训队论文<<最小割模型在信息学竞赛中的应用>>里有完整的建模推导证明,建议搜了学习下~这里直接用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;
}