poj 1815 Friendship 【最小割点集】【枚举删点 + 求解最小字典序】

Friendship
Time Limit: 2000MS Memory Limit: 20000K
Total Submissions: 9923 Accepted: 2742

Description

In modern society, each person has his own friends. Since all the people are very busy, they communicate with each other only by phone. You can assume that people A can keep in touch with people B, only if 
1. A knows B's phone number, or 
2. A knows people C's phone number and C can keep in touch with B. 
It's assured that if people A knows people B's number, B will also know A's number. 

Sometimes, someone may meet something bad which makes him lose touch with all the others. For example, he may lose his phone number book and change his phone number at the same time. 

In this problem, you will know the relations between every two among N people. To make it easy, we number these N people by 1,2,...,N. Given two special people with the number S and T, when some people meet bad things, S may lose touch with T. Your job is to compute the minimal number of people that can make this situation happen. It is supposed that bad thing will never happen on S or T. 

Input

The first line of the input contains three integers N (2<=N<=200), S and T ( 1 <= S, T <= N , and S is not equal to T).Each of the following N lines contains N integers. If i knows j's number, then the j-th number in the (i+1)-th line will be 1, otherwise the number will be 0. 

You can assume that the number of 1s will not exceed 5000 in the input. 

Output

If there is no way to make A lose touch with B, print "NO ANSWER!" in a single line. Otherwise, the first line contains a single number t, which is the minimal number you have got, and if t is not zero, the second line is needed, which contains t integers in ascending order that indicate the number of people who meet bad things. The integers are separated by a single space. 

If there is more than one solution, we give every solution a score, and output the solution with the minimal score. We can compute the score of a solution in the following way: assume a solution is A1, A2, ..., At (1 <= A1 < A2 <...< At <=N ), the score will be (A1-1)*N^t+(A2-1)*N^(t-1)+...+(At-1)*N. The input will assure that there won't be two solutions with the minimal score. 

Sample Input

3 1 3
1 1 0
1 1 1
0 1 1

Sample Output

1

2

犯了非常二的错误,导致WA到死。在求解最小割时用的是Maxflow(s, T),而我建的图应该是Maxflow(s, T+N)。醉了。。。

题意:给你N个点,又给出一个矩阵代表点与点之间的关系。若Map[i][j] = 1,表示i连j。现在问你最少去掉几个点使得S与T不连通,并输出字典序最小的方案.若无法使得S和T不连通,输出NO ANSWER!。

思路:

1,先拆点建图求解最小割ans;

2,枚举所有点i(i != S || i != T && 1 <= i <= N),删去点i后建新图跑一次最小割,得到新割tt。

若tt < ans,说明删去的点在割点集里面,记录并更新ans = tt。

反之说明该点不在割点集里面,复原该点,继续枚举下一个点。

因为我们每次都是贪心的选择编号较小的点入割点集,所以若最小割存在,在枚举完所有点后,就可以得到字典序最小的方案。

简单说下建图:

1,对于点i(i != S || i != T && 1 <= i <= N),建边i到i+N,容量为1;

2,对于点S和T,建边S->S+N和T->T+N,容量为无穷大;

3,对于可达关系<i, j>,建边i->j,容量为1。

注意:我的建图方案中源点是S,汇点是T+N。

AC代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>
#define MAXN 500
#define MAXM 20000
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int dist[MAXN], cur[MAXN];
bool vis[MAXN];
int N, S, T;
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w)
{
    Edge E1 = {u, v, w, 0, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
int Map[201][201];
void input()
{
    memset(Map, 0, sizeof(Map));
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= N; j++)
            scanf("%d", &Map[i][j]);
    }
}
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 getMap()
{
    init();
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= N; j++)
        {
            if(Map[i][j])
            {
                if(i == j)
                {
                    if(i == S || i == T)
                        addEdge(i, i+N, INF);
                    else
                        addEdge(i, i+N, 1);
                }
                else
                    addEdge(i+N, j, INF);
            }
        }
    }
}
int rec[MAXN];//记录要去掉的点集
int top;
int temp[201][201];
void solve()
{
    if(Map[S][T])//无法使得S和T不连通
    {
        printf("NO ANSWER!\n");
        return ;
    }
    getMap();
    int ans = Maxflow(S, T+N);//最小割
    if(ans == 0)
    {
        printf("0\n");
        return ;
    }
    top = 0;
    for(int i = 1; i <= N; i++)
    {
        if(i == S || i == T) continue;
        for(int j = 1; j <= N; j++)
        {
            temp[i][j] = Map[i][j];
            temp[j][i] = Map[j][i];
            Map[i][j] = Map[j][i] = 0;//删点
        }
        getMap();
        int tt = Maxflow(S, T+N);
        if(tt < ans)//最小割减少
            rec[top++] = i, ans = tt;//存储
        else
        {
            for(int j = 1; j <= N; j++)//恢复
                Map[i][j] = temp[i][j], Map[j][i] = temp[j][i];
        }
    }
    printf("%d\n", top);
    for(int i = 0; i < top; i++)
    {
        if(i) printf(" ");
        printf("%d", rec[i]);
    }
    printf("\n");
}
int main()
{
    while(scanf("%d%d%d", &N, &S, &T) != EOF)
    {
        input();
        solve();
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值