hdu 3251 Being a Hero(最小割,最大权闭包+输出割边)

46 篇文章 0 订阅

Being a Hero

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 806    Accepted Submission(s): 255
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
题意:给出一个有向图,给出哪些点供选择,供选择的点都有一个权值,然后要破坏掉一些边,使得起点不能到达任何一个你选择的点,每条边被破坏都要付出相应的代价。现在要选择一些点,破坏一些边,使得最后获得的权值最大,输出这个权值,并输出破坏的哪些边。
思路:最后结果=正总权值-最小的代价。构图如下:
先按原图建边,然后对于能选择的点i,连边i->汇点,容量为该点权值。总权值-最小割即为所获得的最大权值。
最后对残余网络dfs染色,即可找出割边。
AC代码:
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <queue>
#include <stack>
#include <ctime>
#include <vector>
#include <algorithm>
#define ll __int64

using namespace std;

const int INF = 1000000000;
const int maxn = 10005;

struct Edge{
    int u, v, cap, flow, next;
}et[1000000];
int low[maxn], dis[maxn], cnt[maxn], pre[maxn], cur[maxn], eh[maxn], col[maxn];
int s, t, n, m, num, f;
void init(){
    memset(eh, -1, sizeof(eh));
    num = 0;
}
void add(int u, int v, int cap, int flow){
    Edge e = {u, v, cap, flow, eh[u]};
    et[num] = e;
    eh[u] = num++;
}
void addedge(int u, int v, int cap){
    add(u, v, cap, 0);
    add(v, u, 0, 0);
}
int isap(int s, int t, int nv){
    int u, v, now, flow = 0;
    memset(low, 0, sizeof(low));
    memset(cnt, 0, sizeof(cnt));
    memset(dis, 0, sizeof(dis));
    for(u = 0; u <= nv; u++) cur[u] = eh[u];
    low[s] = INF, cnt[0] = nv, u = s;
    while(dis[s] < nv)
    {
        for(now = cur[u]; now != -1; now = et[now].next)
        if(et[now].cap - et[now].flow && dis[u] == dis[v = et[now].v] + 1) break;
        if(now != -1)
        {
            cur[u] = pre[v] = now;
            low[v] = min(low[u], et[now].cap - et[now].flow);
            u = v;
            if(u == t)
            {
                for(; u != s; u = et[pre[u]].u)
                {
                    et[pre[u]].flow += low[t];
                    et[pre[u]^1].flow -= low[t];
                }
                flow += low[t];
                low[s] = INF;
            }
        }
        else
        {
            if(--cnt[dis[u]] == 0) break;
            dis[u] = nv, cur[u] = eh[u];
            for(now = eh[u]; now != -1; now = et[now].next)
            if(et[now].cap - et[now].flow && dis[u] > dis[et[now].v] + 1)
            dis[u] = dis[et[now].v] + 1;
            cnt[dis[u]]++;
            if(u != s) u = et[pre[u]].u;
        }
    }
    return flow;
}
void dfs(int u){
    col[u] = 1;
    for(int i = eh[u]; i != -1; i = et[i].next)
    if(et[i].cap - et[i].flow)
    {
        int v = et[i].v;
        if(!col[v]) dfs(v);
    }
}
int main()
{
    int tt, c = 0, u, v, w;
    scanf("%d", &tt);
    while(tt--)
    {
        scanf("%d%d%d", &n, &m, &f);
        init();
        s = 1, t = n + 1;
        int sum = 0;
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d", &u, &v, &w);
            addedge(u, v, w);
        }
        while(f--)
        {
            scanf("%d%d", &u, &w);
            addedge(u, t, w);
            sum += w;
        }
        printf("Case %d: %d\n", ++c, sum - isap(s, t, t + 1));

        memset(col, 0, sizeof(col));
        dfs(s);
        int ans[100005], tot = 0;
        for(int i = 0; i < 2 * m; i += 2)
        {
            int u = et[i].u, v = et[i].v;
            if(col[u] == 1 && col[v] == 0) ans[tot++] = i / 2 + 1;
        }

        printf("%d", tot);
        for(int i = 0; i < tot; i++) printf(" %d", ans[i]);
        puts("");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值