这题求得是点连通度,或最小割点集。删除这个集合,S到T就不连通,删除这个集合的任意真子集,S到T仍然有路可走。
做法是拆点,将每个人p拆成两个点p和p',令p' = p + N, 建边<p', p, 1>,1为容量,其余的,如果A有B的号码,建边<A,B',INF>,最后求出S到T'的最大流即可(不同的建边情况不同,这里是S到T')。
还有一难点是如果有多组解,输出score最小的一个,其实就是输出字典序最小的一个。按升序枚举,这个每次删除一个点,删除后,如果流量减少,即为最小割割点,记录一下,如果没有减少,则恢复这个点。当然,如果S和T直接相连,输出NO ANSWER!。
代码
#include<stdio.h>
#include<string.h>
#define INF 0x3fffffff
#define MM 15004
#define NN 404
typedef struct node{
int v, c;
struct node *nxt, *op;
}NODE;
NODE edg[MM];
NODE *Link[NN];
int idx, n, S, T, N;
int h[NN];
int num[NN];
int mark[NN];
int flag[NN][NN];
void Add(int u, int v, int c1, int c2){
idx++;
edg[idx].v = v;
edg[idx].c = c1;
edg[idx].nxt = Link[u];
edg[idx].op = edg + idx + 1;
Link[u] = edg + idx;
idx++;
edg[idx].v = u;
edg[idx].c = c2;
edg[idx].nxt = Link[v];
edg[idx].op = edg + idx - 1;
Link[v] = edg + idx;
}
int aug(int u, int flow){
int f;
int l = flow;
int tmp = n - 1;
if (u == T + N) return flow;
for (NODE *p = Link[u]; p; p = p->nxt){
if (p->c && h[u] == h[p->v] + 1){
f = aug(p->v, p->c < l ? p->c : l);
l -= f;
p->c -= f;
p->op->c += f;
if (!l || h[S] == n) return flow - l;
}
if (p->c && h[p->v] < tmp) tmp = h[p->v];
}
if (l == flow){
if (!--num[h[u]]) h[S] = n;
else ++num[h[u] = tmp + 1];
}
return flow - l;
}
int Sap(){
int ans = 0;
n = 2 * N;
memset(h, 0, sizeof(h));
memset(num, 0, sizeof(num));
num[0] = n;
while(h[S] < n){
ans += aug(S, INF);
}
return ans;
}
void CreatGraph(){
int i, j, ii;
idx = 0;
memset(Link, 0, sizeof(Link));
for (i = 1; i <= N; i++){
if (mark[i]) continue;
for (j = 1; j <= N; j++){
if (mark[j]) continue;
if (i != j && flag[i][j]){
ii = i + N;
Add(j, ii, INF, 0);
}
}
}
for (i = 1; i <= N; i++){
if (mark[i]) continue;
ii = i + N;
Add(ii, i, 1, 0);
}
}
int main()
{
int i, j, ans, tmp, t, first;
scanf("%d%d%d", &N, &S, &T);
for (i = 1; i <= N; i++){
for (j = 1; j <= N; j++){
scanf("%d", &flag[i][j]);
}
}
if(flag[S][T] == 1){
puts("NO ANSWER!");
return 0;
}
memset(mark, 0, sizeof(mark));
CreatGraph();
ans = Sap();
tmp = ans;
if (ans == 0){
puts("0");
return 0;
}
for (i = 1; i <= N; i++){
if(i == S || i == T) continue;
mark[i] = 1;
CreatGraph();
t = Sap();
if (t < tmp){
tmp = t;
}else{
mark[i] = 0;
}
}
printf("%d\n", ans);
first = 1;
for (i = 1; i <= N; i++){
if(mark[i])
if(first){
first = 0;
printf("%d", i);
}
else printf(" %d", i);
}
puts("");
return 0;
}