hdoj 3251 Being a Hero 【建图后求解最小割 + 输出任意一组最小割里面边 的编号】

来源:互联网 发布:山东省青岛市 阿里云 编辑:程序博客网 时间:2024/06/04 18:34



Being a Hero

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1252    Accepted Submission(s): 395
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
24 4 21 2 21 3 33 2 42 4 12 34 44 4 21 2 21 3 33 2 12 4 12 34 4
 

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



犯二了,坐两个多小时的车。累死啦。/(ㄒoㄒ)/~~


题意:有N个城市(编号从1到N)和连接城市的M条有向边。你可以选择F个城市,但要求城市1不能到达这F个城市,因此你需要破坏一些边。现在给你破坏每条边的花费以及F个城市的价值,问你能得到的最大价值,并输出需要破坏的边数以及该边的编号(若有多种方案,可以输出任意一种)。


思路:求最小割,最大价值就是F个城市的总价值-最小割。至于输出最小割里面边的编号,只需在残量网络里找到城市1能到达的所有点,这些点必定构成一个集合S。最后遍历M条有向边的正向弧,若弧的起点在S集并且终点不在,那么该弧就是最小割里面的一条边。


建图:设置超级源点source,超级汇点sink

1,有向边<u, v>建边,边权为破坏该边的花费;

2,source向城市1建边,容量为无穷大;

3,选择的F个城市向sink建边,容量为该城市的价值。

source->sink跑一次最大流 即求出最小割。


AC代码:

#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#define MAXN 1010#define MAXM 300000+10#define INF 0x3f3f3f3fusing namespace std;struct Edge{    int from, to, cap, flow, ID, next;//ID记录边的编号};Edge edge[MAXM];int head[MAXN], edgenum;int dist[MAXN], cur[MAXN];bool vis[MAXN];int N, M, F;int source, sink;void init(){    edgenum = 0;    memset(head, -1, sizeof(head));}void addEdge(int u, int v, int w, int id){    Edge E1 = {u, v, w, 0, id, head[u]};    edge[edgenum] = E1;    head[u] = edgenum++;    Edge E2 = {v, u, 0, 0, id, head[v]};    edge[edgenum] = E2;    head[v] = edgenum++;}int val;//所有能获取城市的价值void getMap(){   source = 0, sink = N+1;   int a, b, c;   for(int i = 1; i <= M; i++)   {       scanf("%d%d%d", &a, &b, &c);       addEdge(a, b, c, i);//注意是有向边       //addEdge(b, a, c, i);   }   val = 0;   //M条后 其它的边都是虚拟的边 默认ID为0   for(int i = 1; i <= F; i++)   {       scanf("%d%d", &a, &c);       val += c;       addEdge(a, sink, c, 0);   }   addEdge(source, 1, INF, 0);//source 向 城市1建边}bool BFS(int s, int t){    queue<int> Q;    memset(dist, -1, sizeof(dist));    memset(vis, false, sizeof(vis));    dist[s] = 0;    vis[s] = true;    Q.push(s);    while(!Q.empty())    {        int u = Q.front();        Q.pop();        for(int i = head[u]; i != -1; i = edge[i].next)        {            Edge E = edge[i];            if(!vis[E.to] && E.cap > E.flow)            {                dist[E.to] = dist[u] + 1;                if(E.to == t) return true;                vis[E.to] = true;                Q.push(E.to);            }        }    }    return false;}int DFS(int x, int a, int t){    if(x == t || a == 0) return a;    int flow = 0, f;    for(int &i = cur[x]; i != -1; i = edge[i].next)    {        Edge &E = edge[i];        if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(a, E.cap - E.flow), t)) > 0)        {            edge[i].flow += f;            edge[i^1].flow -= f;            flow += f;            a -= f;            if(a == 0) break;        }    }    return flow;}int Maxflow(int s, int t){    int flow = 0;    while(BFS(s, t))    {        memcpy(cur, head, sizeof(head));        flow += DFS(s, INF, t);    }    return flow;}void find_S(int u)//在残量网络里面 找源点能到的S集{    for(int i = head[u]; i != -1; i = edge[i].next)    {        Edge E = edge[i];        if(vis[E.to]) continue;        if(E.cap > E.flow)//不能满流        {            vis[E.to] = true;            find_S(E.to);        }    }}int rec[100000+1];//记录最小割里面边的编号void output(){    int ans = 0;//统计要去掉的边数    memset(vis, false, sizeof(vis));    find_S(1);//要从1开始    for(int i = 0; i < edgenum; i+=2)//遍历正向弧    {        if(edge[i].ID == 0)//出现ID为0的边 意味着M条实边已经遍历完了             break;        if(vis[edge[i].from] && !vis[edge[i].to])//起点在S集 终点不在            rec[ans++] = edge[i].ID;//记录    }    printf("%d", ans);    for(int i = 0; i < ans; i++)        printf(" %d", rec[i]);    printf("\n");}int main(){    int t, k = 1;    scanf("%d", &t);    while(t--)    {        scanf("%d%d%d", &N, &M, &F);        init();        getMap();        printf("Case %d: %d\n", k++, val - Maxflow(source, sink));        output();    }    return 0;}


1 0
原创粉丝点击