hdoj 4265 Science! 【二分 + 最大流】【求最大完美匹配数并输出完美匹配】



Science!

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 439    Accepted Submission(s): 119
Special Judge


Problem Description
Welcome, ladies and gentlemen, to Aperture Science. Astronauts, War Heroes, Olympians — you’re here because we want the best, and you are it. That said, it’s time to make some science.
Now, I want each of you to stand on one of these buttons. Well done, we’re making great progress here. Now let’s do it again. Oh, come on - don’t stand on the same button! Move, people! No, no, that button’s only for the Astronauts, you know who you are. What?! You say you can’t do everything I ask? Ok let’s start over. You there, the Programmer, figure out how many times we can do this. And make it quick, we have a lot more science to get through…
 

Input
There will be several test cases in the input. The first line of each case will contain  n (2≤ n≤80) giving the number of people (and the number of buttons) in the experiment. The next  n lines will contain  n characters each. If the j th character of the i th line is  Y it indicates that the i th person can stand on the j th button (it is  Notherwise). The last line of input will be a 0.
 

Output
For each test case, output  k, the maximum number of times everyone can be standing on buttons such that nobody stands on the same button more than once (This might be 0). After that, output  k lines. Each line should contain  n integers separated by single spaces, where the i th integer describes which person is standing on the i th button. All of the lines should be valid and none of them should put the same person on the same button as a previous line of the same test case. Output no extra spaces, and do not separate answers with blank lines. Note that correct outputs might not be unique.
 

Sample Input
      
      
3 YYY NYY YNY 2 YN YN 0
 

Sample Output
      
      
2 3 1 2 1 2 3 0
 



题意:有N个人和N个位置。给出一个N*N的矩阵,矩阵第i行第j列的字符为Y时则表示第i个人可以坐第j个位置,反之不能。现在要求每个人都找到一个不同的位置,当他们全部找到位置时,一轮游戏结束,他们可以开始新的一轮。在新的一轮里,每个人依旧会去找一个不同的位置,但是每个人不能找已经坐过的位置。

问一共可以进行多少轮这样的游戏,并输出每一轮中1——N位置所在的人。


注意:不能在求出最大方案数ans后,直接跑ans次最大流——找满流边。这样找到的满流边可能不是完美匹配的边。


简单说下对mid值建图:设置超级源点source,超级汇点sink

1,source向每个人建边,容量为mid;

2,每个人向他可以站的位置建边(这里的关系可能由于边的删去而不存在),容量为1;

3,每个位置向sink建边,容量为mid。



实现:

1,二分枚举mid值,建图后判断是否满流,继而求出最大方案数ans;

2,以ans建边,跑一次最大流,去掉非满流边(在N*N矩阵里面修改关系)。

3,跑ans次最大流,每次均以1建边。在跑完一次最大流后,找到所有以人为起点、以位置为终点的满流边,每一条边都对应着一个匹配关系。

4,删边,对于人i为起点,位置j为终点的满连边,在N*N矩阵里面去掉他们的关系。




AC代码:


#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN 200
#define MAXM 50000
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum, cur[MAXN];
char Map[MAXN][MAXN];
int dist[MAXN];
bool vis[MAXN];
int source, sink;
int N;
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 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++;
}
void getMap(int mid)
{
    for(int i = 1; i <= N; i++)
    {
        addEdge(source, i, mid);
        addEdge(i+N, sink, mid);
    }
    for(int i = 0; i < N; i++)
    {
        for(int j = 0; j < N; j++)
        {
            if(Map[i][j] == 'Y')
                addEdge(i+1, j+1+N, 1);
        }
    }
}
int main()
{
    while(scanf("%d", &N), N)
    {
        for(int i = 0; i < N; i++)
            scanf("%s", Map[i]);
        int l = 0, r = N;
        source = 0, sink = 2*N+1;
        int ans = 0;
        while(r >= l)//二分 求最大方案数
        {
            int mid = (l + r) >> 1;
            init();
            getMap(mid);
            if(Maxflow(source, sink) == mid * N)
            {
                ans = mid;
                l = mid + 1;
            }
            else
                r = mid - 1;
        }
        init();
        getMap(ans);//以ans建边
        Maxflow(source, sink);
        for(int i = 1; i <= N; i++)//删去多余的边
        {
            for(int j = head[i]; j != -1; j = edge[j].next)
            {
                Edge E = edge[j];
                if(E.to == source) continue;
                if(E.cap - E.flow == 1)
                    Map[i-1][E.to-N-1] = 'N';//修改关系
            }
        }
        printf("%d\n", ans);
        int num[MAXN];//记录第i个位置是哪个人
        for(int i = 0; i < ans; i++)
        {
            init();
            getMap(1);//以1为容量建边
            Maxflow(source, sink);
            for(int k = 1; k <= N; k++)//枚举每个人
            {
                for(int j = head[k]; j != -1; j = edge[j].next)//找满流的边
                {
                    Edge E = edge[j];
                    if(E.to == source) continue;
                    if(E.cap - E.flow == 0)//只会有一条满流的边
                    {
                        num[E.to-N] = k;
                        Map[k-1][E.to-1-N] = 'N';//删除关系
                        break;
                    }
                }
            }
            for(int j = 1; j <= N; j++)
            {
                if(j > 1) printf(" ");
                printf("%d", num[j]);
            }
            printf("\n");
        }
    }
    return 0;
}


Science!

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 439    Accepted Submission(s): 119
Special Judge


Problem Description
Welcome, ladies and gentlemen, to Aperture Science. Astronauts, War Heroes, Olympians — you’re here because we want the best, and you are it. That said, it’s time to make some science.
Now, I want each of you to stand on one of these buttons. Well done, we’re making great progress here. Now let’s do it again. Oh, come on - don’t stand on the same button! Move, people! No, no, that button’s only for the Astronauts, you know who you are. What?! You say you can’t do everything I ask? Ok let’s start over. You there, the Programmer, figure out how many times we can do this. And make it quick, we have a lot more science to get through…
 

Input
There will be several test cases in the input. The first line of each case will contain  n (2≤ n≤80) giving the number of people (and the number of buttons) in the experiment. The next  n lines will contain  n characters each. If the j th character of the i th line is  Y it indicates that the i th person can stand on the j th button (it is  Notherwise). The last line of input will be a 0.
 

Output
For each test case, output  k, the maximum number of times everyone can be standing on buttons such that nobody stands on the same button more than once (This might be 0). After that, output  k lines. Each line should contain  n integers separated by single spaces, where the i th integer describes which person is standing on the i th button. All of the lines should be valid and none of them should put the same person on the same button as a previous line of the same test case. Output no extra spaces, and do not separate answers with blank lines. Note that correct outputs might not be unique.
 

Sample Input
       
       
3 YYY NYY YNY 2 YN YN 0
 

Sample Output
       
       
2 3 1 2 1 2 3 0
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值