poj2987 最大权闭合图

求最大权闭合图, 实际是 所有正数相加减去最大流
建边 从源点连向所有正数的点权值为那个正数,所有
负权值的点连向终点一条该点负权值的绝对值的边,
可以想成源点所有出发 本来是有个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,
最后求得的最大流%很大的数就是最少的边数了
*/

猜你喜欢

转载自blog.csdn.net/hyacinthhome/article/details/81507492