求最大权闭合图, 实际是 所有正数相加减去最大流
建边 从源点连向所有正数的点权值为那个正数,所有
负权值的点连向终点一条该点负权值的绝对值的边,
可以想成源点所有出发 本来是有个sum正数,然后由于
各种限制,流量不断缩小,所有连向源点的正边的w不断增大
那么答案就不断增大,最少的点数,可以从源点出发,忽略满流
的边记录走过的点,ans1++。画图好理解些,
拓展:如果求最小割的最小边数,可以在每个边乘很大的数加1,
最后求得的最大流%很大的数就是最少的边数了
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const ll maxn=1e4;
const ll INF=1e9;
struct Edge
{
ll from,to,cap,flow;
};
vector<Edge> edges;
vector<ll> G[maxn+100];
bool vis[maxn+100];
ll d[maxn+100]; //从起点到i的距离
ll cur[maxn+100]; //当前节点正在考虑的弧
ll m,s,t,ans1,ans2;
bool viss[maxn+100];
void AddEdge(ll from,ll to,ll cap);
bool bfs();
ll dfs(ll x,ll a);
ll Maxflow();
void fdfs(ll u);
int main()
{
ll n,mm,u,v,all=0,val;
scanf("%lld%lld",&n,&mm);
s=0,t=n+1;
for(ll i=1;i<=n;i++)
{
scanf("%lld",&val);
if(val>0)
AddEdge(s,i,val),all+=val;
else if(val<0)
AddEdge(i,t,-val);
}
for(ll i=1;i<=mm;i++)
{
scanf("%lld%lld",&u,&v);
AddEdge(u,v,INF);
}
ans1=0;
ans2=Maxflow();
fdfs(0);
printf("%lld %lld\n",ans1,all-ans2);
return 0;
}
void AddEdge(ll from,ll to,ll cap)
{
edges.push_back((Edge){from,to,cap,0});
edges.push_back((Edge){to,from,0,0});
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool bfs()
{
memset(vis,0,sizeof(vis));
queue<ll> Q;
Q.push(s);
d[s]=0; //这样的话都不需要memset了
vis[s]=1;
while(!Q.empty())
{
ll x=Q.front();Q.pop();
for(ll i=0;i<G[x].size();i++)
{
Edge& e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow)
{
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
ll dfs(ll x,ll a)
{
if(x==t||a==0)
return a;
ll flow=0,f;
for(ll& i=cur[x];i<G[x].size();i++)
{
Edge& e=edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0)
{
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)
break;
}
}
return flow;
}
ll Maxflow()
{
ll flow=0;
while(bfs())
{
memset(cur,0,sizeof(cur));
flow+=dfs(s,INF);
}
return flow;
}
void fdfs(ll u)
{
viss[u]=true;
for(ll i=0;i<G[u].size();i++)
{
Edge e=edges[G[u][i]];
if(viss[e.to]||e.cap-e.flow==0) continue;
ans1++;
fdfs(e.to);
}
}
/*
求最大权闭合图, 实际是 所有正数相加减去最大流
建边 从源点连向所有正数的点权值为那个正数,所有
负权值的点连向终点一条该点负权值的绝对值的边,
可以想成源点所有出发 本来是有个sum正数,然后由于
各种限制,流量不断缩小,所有连向源点的正边的w不断增大
那么答案就不断增大,最少的点数,可以从源点出发,忽略满流
的边记录走过的点,ans1++。画图好理解些,
拓展:如果求最小割的最小边数,可以在每个边乘很大的数加1,
最后求得的最大流%很大的数就是最少的边数了
*/