首页 > 代码库 > ZOJ 3348 Schedule(map运用+网络流之最大流)(竞赛问题升级版)

ZOJ 3348 Schedule(map运用+网络流之最大流)(竞赛问题升级版)

题目地址:ZOJ 3348

仍然是一道竞赛问题的网络流问题,但是这道题再用上次的竞赛建图方法就不行了,5000场比赛,明显会超时,于是需要换种建图思路了。上一道经典竞赛问题戳这里

上一道的胜负转换是利用专门给比赛建一个点,通过对比赛双方的流向来控制胜负关系,这里的建图方法更加巧妙(膜拜想出这个方法的大牛。。。),是先假设其中一方获胜,用mp[a][b]来表示a赢b的次数,将a与b连边,权值为mp[a][b],这样的话,前面的假设就仅仅只是假设而已,因为在这里,如果a的流量流向了b,说明a的胜利果实到了b,相当于b获胜。这是题解上的说法,但是我个人是理解的是在这里把比赛转换成了边(上一道是点),这条边连接比赛双方,然后这条边上的流量通过某一方从源点里加入都行,最后看从哪个端点流向了汇点就是相当于哪边赢,这是我个人的理解。

建图方法就是建一源点与汇点,每个人都与源点连边,权值为这个人将要获胜的场数,与汇点连边,权值为与DD的积分差-1,很明显是用来保证积分不超过DD的,然后将假设的胜负关系用有向边将各点连接起来。最后求一次最大流判断是否满流。对于每个人的转换处理,我是直接用的map映射,这样比较方便。

代码如下;

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <queue>
#include <map>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int head[200], source, sink, nv, cnt;
int cur[200], num[200], d[200], pre[200], q[200], w[200], mp[200][200];
struct node
{
    int u, v, cap, next;
} edge[1000000];
void add(int u, int v, int cap)
{
    edge[cnt].v=v;
    edge[cnt].cap=cap;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].v=u;
    edge[cnt].cap=0;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
void bfs()
{
    memset(num,0,sizeof(num));
    memset(d,-1,sizeof(d));
    int f1=0, f2=0, i;
    d[sink]=0;
    num[0]=1;
    q[f1++]=sink;
    while(f1>=f2)
    {
        int u=q[f2++];
        for(i=head[u]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].v;
            if(d[v]==-1)
            {
                d[v]=d[u]+1;
                num[d[v]]++;
                q[f1++]=v;
            }
        }
    }
}
int isap()
{
    memcpy(cur,head,sizeof(cur));
    bfs();
    int flow=0, u=pre[source]=source, i;
    while(d[source]<nv)
    {
        if(u==sink)
        {
            int f=INF,pos;
            for(i=source; i!=sink; i=edge[cur[i]].v)
            {
                if(f>edge[cur[i]].cap)
                {
                    f=edge[cur[i]].cap;
                    pos=i;
                }
            }
            for(i=source; i!=sink; i=edge[cur[i]].v)
            {
                edge[cur[i]].cap-=f;
                edge[cur[i]^1].cap+=f;
            }
            flow+=f;
            u=pos;
        }
        for(i=cur[u]; i!=-1; i=edge[i].next)
        {
            if(d[edge[i].v]+1==d[u]&&edge[i].cap)
                break;
        }
        if(i!=-1)
        {
            cur[u]=i;
            pre[edge[i].v]=u;
            u=edge[i].v;
        }
        else
        {
            if(--num[d[u]]==0) break;
            int mind=nv;
            for(i=head[u]; i!=-1; i=edge[i].next)
            {
                if(mind>d[edge[i].v]&&edge[i].cap)
                {
                    mind=d[edge[i].v];
                    cur[u]=i;
                }
            }
            d[u]=mind+1;
            num[d[u]]++;
            u=pre[u];
        }
    }
    return flow;
}
int main()
{
    int n, m, p, i, j, z;
    char s1[20], s2[20], s3[20];
    map<string,int>Q;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(w,0,sizeof(w));
        memset(head,-1,sizeof(head));
        cnt=0;
        z=1;
        Q.clear();
        char s4[20]="DD";
        Q[s4]=1;
        for(i=0; i<m; i++)
        {
            scanf("%s%s%s",s1,s2,s3);
            if(!Q[s1])
                Q[s1]=++z;
            if(!Q[s2])
                Q[s2]=++z;
            if(strcmp(s3,"win")==0)
            {
                w[Q[s1]]++;
            }
            else
            {
                w[Q[s2]]++;
            }
        }
        scanf("%d",&p);
        memset(mp,0,sizeof(mp));
        for(i=0; i<p; i++)
        {
            scanf("%s%s",s1,s2);
            if(!Q[s1])
                Q[s1]=++z;
            if(!Q[s2])
                Q[s2]=++z;
            if(Q[s1]==1||Q[s2]==1)
            {
                w[1]++;
            }
            else
            {
                mp[Q[s1]][Q[s2]]++;
                w[Q[s1]]++;
            }
        }
        int sum=0;
        for(i=2;i<=z;i++)
            sum+=w[i];
        source=0;
        sink=z;
        nv=sink+1;
        for(i=2; i<=z; i++)
        {
            add(source,i-1,w[i]);
            add(i-1,sink,w[1]-1);
            for(j=2; j<=z; j++)
            {
                add(i-1,j-1,mp[i][j]);
            }
        }
        int x=isap();
        //printf("%d\n",x);
        if(x==sum)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}


ZOJ 3348 Schedule(map运用+网络流之最大流)(竞赛问题升级版)