zoukankan      html  css  js  c++  java
  • poj 2125 Destroying The Graph (最小点权覆盖)

    Destroying The Graph
    Time Limit: 2000MS   Memory Limit: 65536K
             

    Description

    Alice and Bob play the following game. First, Alice draws some directed graph with N vertices and M arcs. After that Bob tries to destroy it. In a move he may take any vertex of the graph and remove either all arcs incoming into this vertex, or all arcs outgoing from this vertex. 
    Alice assigns two costs to each vertex: Wi+ and Wi-. If Bob removes all arcs incoming into the i-th vertex he pays Wi+ dollars to Alice, and if he removes outgoing arcs he pays Wi- dollars. 
    Find out what minimal sum Bob needs to remove all arcs from the graph.

    Input

    Input file describes the graph Alice has drawn. The first line of the input file contains N and M (1 <= N <= 100, 1 <= M <= 5000). The second line contains N integer numbers specifying Wi+. The third line defines Wi- in a similar way. All costs are positive and do not exceed 106 . Each of the following M lines contains two integers describing the corresponding arc of the graph. Graph may contain loops and parallel arcs.

    Output

    On the first line of the output file print W --- the minimal sum Bob must have to remove all arcs from the graph. On the second line print K --- the number of moves Bob needs to do it. After that print K lines that describe Bob's moves. Each line must first contain the number of the vertex and then '+' or '-' character, separated by one space. Character '+' means that Bob removes all arcs incoming into the specified vertex and '-' that Bob removes all arcs outgoing from the specified vertex.

    Sample Input

    3 6
    1 2 3
    4 2 1
    1 2
    1 1
    3 2
    1 2
    3 1
    2 3
    

    Sample Output

    5
    3
    1 +
    2 -
    2 +
    题目大意:
    n个点m条边的有向图
    需要移走这张图里所有的边
    每次可以选择移走点i的所有入边或所有出边
    每步操作都有对应的代价
    求最小代价移走所有的边
    注:边有自环和平行边

    最小点权覆盖集

    =最小割
    拆点
    源点向每个点连一条流量为outgoing pay的边
    每个点向汇点连一条流量为incoming pay的边
    原图中的边i,j,由i向拆出的j连inf边
    跑最小割
    方案的输出:
    从源点遍历残量网络,边还有流量就遍历,记录所有遍历到的点
    原本就有的点,如果没有被遍历到,就说明它被割了
    拆出的点,如果被遍历到,说明它被割了
    #include<cstdio>
    #include<queue>
    using namespace std;
    int n,m,tot=1,ans;
    int front[11100],to[11100],nextt[11100],cap[11100];
    int lev[210],cur[210];
    int src,decc;
    bool g[210];
    queue<int>q;
    void add(int u,int v,int w)
    {
        to[++tot]=v;nextt[tot]=front[u];front[u]=tot;cap[tot]=w;
        to[++tot]=u;nextt[tot]=front[v];front[v]=tot;cap[tot]=0;
    }
    bool bfs()
    {
        for(int i=0;i<=decc;i++) {lev[i]=-1;cur[i]=front[i];}
        while(!q.empty()) q.pop();
        q.push(src);lev[src]=0;
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=front[now];i;i=nextt[i])
            {
                int t=to[i];
                if(cap[i]>0&&lev[t]==-1)
                {
                    lev[t]=lev[now]+1;
                    q.push(t);
                    if(t==decc) return true;
                }
            }
        }
        return false;
    }
    int dinic(int now,int flow)
    {
        if(now==decc) return flow;
        int rest=0,delta;
        for(int & i=cur[now];i;i=nextt[i])
        {
            int t=to[i];
            if(lev[t]>lev[now]&&cap[i]>0)
            {
                delta=dinic(t,min(flow-rest,cap[i]));
                if(delta)
                {
                    cap[i]-=delta;cap[i^1]+=delta;
                    rest+=delta;if(rest==flow) break;
                }
            }
        }
        if(rest!=flow) lev[now]=-1;
        return rest;
    }
    void cut(int now)
    {
        g[now]=true;
        for(int i=front[now];i;i=nextt[i])
        {
            if(cap[i]==0||g[to[i]]) continue;
            cut(to[i]);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        decc=n+1<<1;
        int x,y;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            add(i<<1|1,decc,x);
        }
        for(int i=1;i<=n;i++) 
        {
            scanf("%d",&x);
            add(src,i<<1,x);
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            add(x<<1,y<<1|1,2e9);
        }
        while(bfs()) ans+=dinic(src,2e9); 
        printf("%d
    ",ans);
        int sum=0;
        cut(src);
        for(int i=1;i<=n;i++)
        {
            if(g[i<<1|1]) sum++;
            if(!g[i<<1]) sum++;
        }
        printf("%d
    ",sum);
        for(int i=1;i<=n;i++)
        {
            if(!g[i<<1]) printf("%d -
    ",i);
            if(g[i<<1|1]) printf("%d +
    ",i);
        
        }
    }

    错误:

    1、

    应该是

    源点向每个点连一条流量为outgoing pay的边

    每个点向汇点连一条流量为incoming pay的边

    连反了

    与源点相连的点,连出去的边是点打出的,所以源点与点之间的边控制的是出边的流量

    汇点同理

    2、方案输出方法错误

    错误方法:

    在残量网络中,如果与源点相连的边流量为0,说明这个点被割了

    如果汇点连出去的边的流量 为这条边指 向的点的原流量,说明这个点被割了

    前半部分是正确的,但后半部分是错的

    因为跑最大流过程中,增光路上所有边流量都减,

    比如有一条边由1指向2,所有花费都是1

    跑完最大流后,源点——1 残量为0

    2——汇点 残量为0

    最终判断的是割掉2个点,但实际割其中一个就行

    /*for(int i=front[src];i;i=nextt[i])
        {
            if(cap[i]==0)
            {
                sum++;
                a[sum][0]=to[i]/2;a[sum][1]='+';
            }
        }
        for(int i=front[decc];i;i=nextt[i])
        {
            if(cap[i]==out[to[i]/2])
            {
                sum++;
                a[sum][0]=to[i]/2;a[sum][1]='-';
            }
        }*/
    错误代码

    3、题目中说有自环,做的时候把它特判去掉了,错

  • 相关阅读:
    克如斯卡尔 P1546
    真正的spfa
    第四课 最小生成树 要点
    关于vscode中nullptr未定义
    cmake学习笔记
    python学习笔记
    (BFS 图的遍历) 2906. kotori和迷宫
    (图论基础题) leetcode 997. Find the Town Judge
    (BFS DFS 并查集) leetcode 547. Friend Circles
    (BFS DFS 图的遍历) leetcode 841. Keys and Rooms
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6511146.html
Copyright © 2011-2022 走看看