zoj 3348 Schedule 【最大流经典建模】 【好题】

Schedule

Time Limit: 1 Second      Memory Limit: 32768 KB

DD enters the top-level pingpong match! As the game going on, he wonders if there is possibility for him to get the champion(excluding joint champions). Now provide you the results of previous matches, and the details of rest matches. You have to judge it.

Input

There are several test cases. You should process to the end of input.

For each test case. the first line contain two integers n (1 <= n <= 50) and m(0 <= m <= 1000), n indicates the number of players. m indicates the matches have ended.

Then for the following m lines, each line has the format: "Player1 Player2 Result". Player1 and Player2 are the names of the two players, and Result could only be "win" or "lose".

In the next line there is another integer p (0 <= p <= 5000), indicates the matches that will start later.

Then for the following p lines, each line has the format: "Player1 Player2", which means Player1 and Player2 will start a match.

We ensure there is a player named "DD". The length of each player name is no longer than 10.

Output

You should only output "Yes" or "No" for each case, which means if DD has the possibility to be the champion.

Sample Input

3 2
DD winsty win
DD owen lose
2
winsty owen
DD owen
3 3
DD winsty win
DD owen lose
winsty owen lose
2
owen winsty
owen DD

Sample Output

Yes

No

题意:有N个参赛队员,其中一人DD想拿比赛冠军。冠军是赢的场次最多的那个人(不能有和他平齐的)。现在知道M场已经结束的比赛,如 A B win 说明A赢了B 或者A B lose说明A输给B。又给了P场将要进行的比赛以及比赛双方,问DD有没有希望拿冠军。

思路:假设剩下的P场比赛中DD参加了a场,在之前的M场比赛中胜利了b场,我们可以认为这a场都是DD胜利(当然要用临界情况判断,想一下,若下面全赢DD还不是冠军 就肯定没有希望了),这样DD胜利了a+b场。这样问题转化为能否将剩下的P-a场比赛(也就是剩下的P场比赛中DD没有参与的比赛)的胜利分配给除了DD以外的所有n-1人,使得除DD外任意一人的胜场数都小于DD的胜场数。

建图:设源点source,汇点sink,把每场比赛虚拟成一个节点。
一:从source向 所有P-a场比赛 引一条容量为1的边;
二:因为每场比赛只有一个胜者,所以每场比赛要向参加比赛的两个选手连边,容量为1,代表这个比赛的胜利只能由两人中的一人获得。
三:除DD外每个人向sink连边,容量为a + b - 1,代表每个人的胜利次数不能超过DD胜利的次数。
然后对这个图求最大流,如果最大流小于P-a则代表无法将P-a场比赛的胜利分配给n-1人,则输出NO。

AC代码:

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <algorithm>
#define MAXN 6000+10
#define MAXM 200000
#define INF 10000000
using namespace std;
struct Edge
{
    int from, to, cap, flow, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum, cur[MAXN];
int dist[MAXN];
bool vis[MAXN];
map<string, int> fp;//建立队员与编号的映射
int k;//记录队员编号
int N, M, P;
int source, sink;//超级源点 超级汇点
int win[MAXN];//记录 赢了多少场比赛
bool flag;
int num;//记录剩下的比赛里面没有DD的场次
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()
{
    k = 1;
    fp.clear();
    char op[20], a[20], b[20];
    memset(win, 0, sizeof(win));
    fp["DD"] = 1;//DD编号 为 1
    while(M--)
    {
        scanf("%s%s%s", a, b, op);
        if(!fp[a]) fp[a] = ++k;
        if(!fp[b]) fp[b] = ++k;
        if(op[0] == 'w')
            win[fp[a]]++;
        else
            win[fp[b]]++;
    }
    scanf("%d", &P);
    source = 0, sink = N + P + 1;//超级源点 超级汇点
    int DDwin = win[1];//记录DD最多赢多少场
    num = 0;
    for(int i = 1; i <= P; i++)
    {
        scanf("%s%s", a, b);
        if(!fp[a]) fp[a] = ++k;
        if(!fp[b]) fp[b] = ++k;
        if(strcmp(a, "DD") == 0 || strcmp(b, "DD") == 0)
        {
            DDwin++;//假设DD下面所有比赛全都赢
            continue;
        }
        num++;
        addEdge(source, i + N, 1);//源点向每一场比赛引一条容量为1的边
        addEdge(i + N, fp[a], 1);//每场比赛向比赛双方建边 容量为1
        addEdge(i + N, fp[b], 1);
    }
    flag = true;
    for(int i = 2; i <= k; i++)
    {
        if(DDwin <= win[i])//就剩下面的比赛都赢DD也不是冠军
        {
            //printf("%d %d\n", DDwin, win[i]);
            flag = false;
            return ;
        }
    }
    for(int i = 2; i <= k; i++)//参加比赛的人赢的场数必须小于DD的 
        addEdge(i, sink, DDwin - 1 - win[i]);//减去该选手以前获得的胜利
}
bool BFS(int start, int end)
{
    queue<int> Q;
    memset(dist, -1, sizeof(dist));
    memset(vis, false, sizeof(vis));
    dist[start] = 0;
    vis[start] = true;
    Q.push(start);
    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)
            {
                vis[E.to] = true;
                dist[E.to] = dist[u] + 1;
                if(E.to == end) return true;
                Q.push(E.to);
            }
        }
    }
    return false;
}
int DFS(int x, int a, int end)
{
    if(x == end || 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(E.cap-E.flow, a), end)) > 0)
        {
            E.flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}
int Maxflow(int start, int end)
{
    int flow = 0;
    while(BFS(start, end))
    {
        memcpy(cur, head, sizeof(head));
        flow += DFS(start, INF, end);
    }
    return flow;
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        init();
        getMap();
        if(!flag)
        {
            printf("No\n");
            continue;
        }
        if(Maxflow(source, sink) < num)//不能合理分配DD未参加的num场比赛
            printf("No\n");
        else
            printf("Yes\n");
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值