zoukankan      html  css  js  c++  java
  • Being a Hero (hdu 3251 最小割 好题)

    Being a Hero

    Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 1211    Accepted Submission(s): 381
    Special Judge


    Problem Description
    You are the hero who saved your country. As promised, the king will give you some cities of the country, and you can choose which ones to own!

    But don't get too excited. The cities you take should NOT be reachable from the capital -- the king does not want to accidentally enter your area. In order to satisfy this condition, you have to destroy some roads. What's worse, you have to pay for that -- each road is associated with some positive cost. That is, your final income is the total value of the cities you take, minus the total cost of destroyed roads.

    Note that each road is a unidirectional, i.e only one direction is available. Some cities are reserved for the king, so you cannot take any of them even if they're unreachable from the capital. The capital city is always the city number 1.
     

    Input
    The first line contains a single integer T (T <= 20), the number of test cases. Each case begins with three integers n, m, f (1 <= f < n <= 1000, 1 <= m < 100000), the number of cities, number of roads, and number of cities that you can take. Cities are numbered 1 to n. Each of the following m lines contains three integers u, v, w, denoting a road from city u to city v, with cost w. Each of the following f lines contains two integers u and w, denoting an available city u, with value w.
     

    Output
    For each test case, print the case number and the best final income in the first line. In the second line, print e, the number of roads you should destroy, followed by e integers, the IDs of the destroyed roads. Roads are numbered 1 to m in the same order they appear in the input. If there are more than one solution, any one will do.
     

    Sample Input
    2 4 4 2 1 2 2 1 3 3 3 2 4 2 4 1 2 3 4 4 4 4 2 1 2 2 1 3 3 3 2 1 2 4 1 2 3 4 4
     

    Sample Output
    Case 1: 3 1 4 Case 2: 4 2 1 3
     

    Source
     

    Recommend
    zhonglihua   |   We have carefully selected several similar problems for you:  3259 3258 3257 3256 3255 


    题意:n个点m条边的有向图,每条边有破坏话花费,如今国王在城市1,要分配给英雄一些城市。分配的原则是:仅仅能在规定的f个城市中选若干个。这f个城市每一个都有一个获利。被选择的城市要与国王所在的城市1隔离,所以选定后要花费一些费用来破坏边。问最后获利的最大值是多少,而且输出要破坏的边的序号。

    思路:这个题拿到手之后非常久没有思路。由于图上既有获利又有花费,不知道怎么建图。无奈仅仅好求助网上神牛。

    加入汇点T,原图上的单向边依次建边,容量为花费,同意选择的f个点向汇点T连边。容量为点上权值。

    跑一遍最小割得到花费值cost,然后用总的能获得利润(就是f个点的权值之和)减去cost就是答案。那么如何确定哪条边是割边呢?从源点S在残留网络中dfs遍历能走到的点。那么这些点就是属于S集,其它剩下的点就属于T集了。然后推断边的两个点所属的集合。假设属于不同的集合那么这条边就是割边。

    这样建边就全然转换成费用了,对于原图上的边假设被割到,那么这条边就是要破坏的,对于和汇点相连的边假设被割到。那么这个城市就是不能选的,最后最小割就是最小费用,感觉这样非常巧妙。

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <string>
    #include <map>
    #include <stack>
    #include <vector>
    #include <set>
    #include <queue>
    #pragma comment (linker,"/STACK:102400000,102400000")
    #define pi acos(-1.0)
    #define eps 1e-6
    #define lson rt<<1,l,mid
    #define rson rt<<1|1,mid+1,r
    #define FRE(i,a,b)  for(i = a; i <= b; i++)
    #define FREE(i,a,b) for(i = a; i >= b; i--)
    #define FRL(i,a,b)  for(i = a; i < b; i++)
    #define FRLL(i,a,b) for(i = a; i > b; i--)
    #define mem(t, v)   memset ((t) , v, sizeof(t))
    #define sf(n)       scanf("%d", &n)
    #define sff(a,b)    scanf("%d %d", &a, &b)
    #define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
    #define pf          printf
    #define DBG         pf("Hi
    ")
    typedef long long ll;
    using namespace std;
    
    #define INF 0x3f3f3f3f
    #define mod 1000000009
    const int maxn = 1005;
    const int MAXN = 2005;
    const int MAXM = 200010;
    const int N = 1005;
    
    int n,m,f;
    
    struct Edge
    {
        int to,next,cap,flow;
    }edge[MAXM];
    
    int tol;
    int head[MAXN];
    int gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN];
    
    void init()
    {
        tol=0;
        memset(head,-1,sizeof(head));
    }
    
    //加边,单向图三个參数。双向图四个參数
    void addedge(int u,int v,int w,int rw=0)
    {
        edge[tol].to=v; edge[tol].cap=w; edge[tol].next=head[u];
        edge[tol].flow=0; head[u]=tol++;
        edge[tol].to=u; edge[tol].cap=rw; edge[tol].next=head[v];
        edge[tol].flow=0; head[v]=tol++;
    }
    
    //输入參数:起点。终点,点的总数
    //点的编号没有影响。仅仅要输入点的总数
    int sap(int start,int end,int N)
    {
        memset(gap,0,sizeof(gap));
        memset(dep,0,sizeof(dep));
        memcpy(cur,head,sizeof(head));
        int u=start;
        pre[u]=-1;
        gap[0]=N;
        int ans=0;
        while (dep[start]<N)
        {
            if (u==end)
            {
                int Min=INF;
                for (int i=pre[u];i!=-1;i=pre[edge[i^1].to])
                    if (Min>edge[i].cap-edge[i].flow)
                        Min=edge[i].cap-edge[i].flow;
                for (int i=pre[u];i!=-1;i=pre[edge[i^1].to])
                {
                    edge[i].flow+=Min;
                    edge[i^1].flow-=Min;
                }
                u=start;
                ans+=Min;
                continue;
            }
            bool flag=false;
            int v;
            for (int i=cur[u];i!=-1;i=edge[i].next)
            {
                v=edge[i].to;
                if (edge[i].cap-edge[i].flow && dep[v]+1==dep[u])
                {
                    flag=true;
                    cur[u]=pre[v]=i;
                    break;
                }
            }
            if (flag)
            {
                u=v;
                continue;
            }
            int Min=N;
            for (int i=head[u];i!=-1;i=edge[i].next)
                if (edge[i].cap-edge[i].flow && dep[edge[i].to]<Min)
                {
                    Min=dep[edge[i].to];
                    cur[u]=i;
                }
            gap[dep[u]]--;
            if (!gap[dep[u]]) return ans;
            dep[u]=Min+1;
            gap[dep[u]]++;
            if (u!=start) u=edge[pre[u]^1].to;
        }
        return ans;
    }
    
    bool vis[MAXN];
    int out[MAXN];
    
    void dfs(int u)
    {
        if (vis[u]) return ;
        vis[u]=true;
        for (int i=head[u];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if (edge[i].cap-edge[i].flow>0)
                dfs(v);
        }
        return ;
    }
    
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("C:/Users/lyf/Desktop/IN.txt","r",stdin);
    #endif
        int i,j,t,u,v,w,cas=0;
        sf(t);
        while (t--)
        {
            init();
            sfff(n,m,f);
            for (i=0;i<m;i++)
            {
                sfff(u,v,w);
                addedge(u,v,w);
            }
            int T=0,all=0;
            for (i=0;i<f;i++)
            {
                sff(u,w);
                addedge(u,T,w);
                all+=w;
            }
            printf("Case %d: %d
    ",++cas,all-sap(1,T,n+1));
            mem(vis,false);
            dfs(1);
            int cnt=0;
            for (i=0;i<2*m;i+=2)
            {
                if (vis[edge[i^1].to]&&!vis[edge[i].to])
                    out[cnt++]=i/2;
            }
            printf("%d",cnt);
            for (i=0;i<cnt;i++)
                pf(" %d",out[i]+1);
            pf("
    ");
        }
        return 0;
    }
    


  • 相关阅读:
    OAuth2.0说明文档
    CentOS直接解压可用的memcached、nginx、keepalived
    CentOS离线安装GCC编译环境
    [交通安全]电动自行车认定为非机动车的文件
    修改sublime列编辑快捷键
    手机号归属地接口
    ubuntu下typora的gitee图床配置-----基于picgo
    spyder无法切换中文输入法
    lightgbm直方图算法
    xgboost原理分析
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/7232432.html
Copyright © 2011-2022 走看看