题意:摘自NOCOW翻译(http://www.nocow.cn/index.php/Translate:USACO/telecow)
描述
农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流。这些机器用如下的方式发送电邮:如果存在一个由c台电脑组成的序列a1,a2,...,a(c),且a1与a2相连,a2与a3相连,等等,那么电脑a1和a(c)就可以互发电邮。
很不幸,有时候奶牛会不小心踩到电脑上,农夫约翰的车也可能碾过电脑,这台倒霉的电脑就会坏掉。这意味着这台电脑不能再发送电邮了,于是与这台电脑相关的连接也就不可用了。
有两头奶牛就想:如果我们两个不能互发电邮,至少需要坏掉多少台电脑呢?请编写一个程序为她们计算这个最小值和与之对应的坏掉的电脑集合。
以如下网络为例:
1* / 3 - 2*
这张图画的是有2条连接的3台电脑。我们想要在电脑1和2之间传送信息。电脑1与3、2与3直接连通。如果电脑3坏了,电脑1与2便不能互发信息了。
[编辑]格式
PROGRAM NAME: telecow
INPUT FORMAT 第一行四个由空格分隔的整数:N,M,c1,c2.N是电脑总数(1<=N<=100),电脑由1到N编号。M是电脑之间连接的总数(1<= M<=600)。最后的两个整数c1和c2是上述两头奶牛使用的电脑编号。连接没有重复且均为双向的(即如果c1与c2相连,那么c2与c1也相连)。两台电脑之间至多有一条连接。电脑c1和c2不会直接相连。 第2到M+1行 接下来的M行中,每行包含两台直接相连的电脑的编号。
OUTPUT FORMAT
输出共有两行。第一行是使电脑c1和c2不能互相通信需要坏掉的电脑数目的最小值。第二行是排好序的坏掉的电脑的编号列表。注意c1和c2都不能坏掉。如果有多种可能情况,输出第一个数最小的一种,如果第一个数相同,则输出第二个数最小的一种,依此类推。
[编辑]样例输入(file telecow.in)
3 2 1 2 1 3 2 3
[编辑]样例输出(file telecow.out)
1 3
解题思路:
- 将每个点拆分为两个点(如i拆分为i1和i2),原图中的边(i, j)在新图中变成四条有向边:(i1, i2) = 1,(j1, j2) = 1,(i2, j1) = INFINITY,(j2, i1) = INFINITY,等号后面为边权
- 源点为c1的第二个点c12,汇点为c2的第一个点c21,求最大流MaxFlow(c12, c21),则为第一问的答案
- 遍历每一条边(i1, i2),i = [1, N]并且c1, c2除外。如果将其去掉后当前最大流减少了1,则代表点i属于最小割集,也就是第二问的答案中
代码:
/*
ID: zc.rene1
LANG: C
PROG: telecow
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_N 100
#define INFINITY 100000
int N, M, c1, c2;
int capacity[2 * MAX_N + 1][2 * MAX_N + 1];
int capacity_cp[2 * MAX_N + 1][2 * MAX_N + 1];
int visited[2 * MAX_N + 1];
int flow[2 * MAX_N + 1];
int parent[2 * MAX_N + 1];
int origin_maxflow;
void GetInput(FILE *fin)
{
int i, p1, p2;
memset(capacity, 0, (2 * MAX_N + 1) * (2 * MAX_N + 1) * sizeof(int));
fscanf(fin, "%d %d %d %d", &N, &M, &c1, &c2);
for (i=1; i<=M; i++)
{
fscanf(fin, "%d %d", &p1, &p2);
capacity[p1][p1 + N] = 1;
capacity[p2][p2 + N] = 1;
capacity[p1 + N][p2] = INFINITY;
capacity[p2 + N][p1] = INFINITY;
}
}
int min(int a, int b)
{
return a < b ? a : b;
}
int MaxFlow(int source, int sink)
{
int total_flow, max_flow, max_loc, path_capacity, current, next_node;
int i;
if (source == sink)
{
return INFINITY;
}
total_flow = 0;
while (1)
{
for (i=1; i<=2*N; i++)
{
flow[i] = 0;
parent[i] = -1;
visited[i] = 0;
}
flow[source] = INFINITY;
while (1)
{
max_flow = 0;
max_loc = -1;
for (i=1; i<=2*N; i++)
{
if (flow[i] > max_flow && visited[i] == 0)
{
max_flow = flow[i];
max_loc = i;
}
}
if (max_loc == -1)
{
break;
}
if (max_loc == sink)
{
break;
}
visited[max_loc] = 1;
for (i=1; i<=2*N; i++)
{
if (capacity_cp[max_loc][i] != 0 && flow[i] < min(max_flow, capacity_cp[max_loc][i]))
{
parent[i] = max_loc;
flow[i] = min(max_flow, capacity_cp[max_loc][i]);
}
}
}
if (max_loc == -1)
{
break;
}
path_capacity = flow[sink];
total_flow += path_capacity;
current = sink;
while (current != source)
{
next_node = parent[current];
capacity_cp[next_node][current] -= path_capacity;
capacity_cp[current][next_node] += path_capacity;
current = next_node;
}
}
return total_flow;
}
void GetNodes(FILE *fout)
{
int i, current_maxflow, prev_maxflow, count = 0;
int capacity_cp2[2 * MAX_N + 1][2 * MAX_N + 1];
memcpy(capacity_cp2, capacity, (2 * MAX_N + 1) * (2 * MAX_N + 1) * sizeof(int));
memcpy(capacity_cp, capacity, (2 * MAX_N + 1) * (2 * MAX_N + 1) * sizeof(int));
prev_maxflow = origin_maxflow;
for (i=1; i<=N; i++)
{
if (i != c1 && i!= c2)
{
memcpy(capacity_cp, capacity_cp2, (2 * MAX_N + 1) * (2 * MAX_N + 1) * sizeof(int));
capacity_cp[i][i + N] = 0;
current_maxflow = MaxFlow(c1 + N, c2);
if (prev_maxflow - current_maxflow == 1)
{
if (count == 0)
{
fprintf(fout, "%d", i);
}
else
{
fprintf(fout, " %d", i);
}
count++;
capacity_cp2[i][i + N] = 0;
prev_maxflow = current_maxflow;
}
}
}
fprintf(fout, "\n");
}
int main(void)
{
FILE *fin, *fout;
fin = fopen("telecow.in", "r");
fout = fopen("telecow.out", "w");
GetInput(fin);
memcpy(capacity_cp, capacity, (2 * MAX_N + 1) * (2 * MAX_N + 1) * sizeof(int));
origin_maxflow = MaxFlow(c1 + N, c2);
fprintf(fout, "%d\n", origin_maxflow);
GetNodes(fout);
return 0;
}