[省选前题目整理][POJ 2699]The Maximum Number of Strong Kings(暴力枚举+最大流)

19 篇文章 0 订阅
3 篇文章 0 订阅

题目链接

http://poj.org/problem?id=2699

题目大意

n个人比赛, 两两比,共n(n-1)场比赛, 赢的人得1分, n<=10。如果一个人打败了所有比自己分数高的人, 或者他本身就是分数最高的, 那么他就是StrongKing。可能有多个Strong King, 现在按非降的顺序给你每个人的得分(不难道想到容量把), 问Strong King最多能有几个。

思路

显然n个人之间的比赛关系可以构成一个竞赛图。竞赛图问题往往可以用网络流解决,此题也是如此。鉴于某个答案的猜测值的可行性随着答案大小单调改变,很自然地想到用二分来解决此题。那么我们就要二分Strong King的个数ans。下面的问题变成了,已知Strong King的个数ans,求这个答案是否大于实际答案(不成立)。

不妨把每个人看成一个个点(如下图的蓝色点),每个比赛也看成一个个点(如下图的绿色点)。每个比赛向T连容量为1的边(表示赢得一场比赛能得到1分),S向每个人连容量为该人获得分数的边(表示这个人最多只能赢这么多分)。

然后对于后ans个人来说,由于输入的分数是升序的,因此它们是我们假定的Strong Kings。在这些人中,如果人i的分比人j低,那么就从人i向这两个人的比赛对应的点连容量为1的边(显然i和j的胜负已定,根据规则,i比j的分低,而i也是Strong King,那么一定是i赢了这场比赛)。对于所有人来说,任意两个人a、b,分别向他们之间的比赛对应的点连容量为1的边(表明对于其他人来说,他们既可以赢,也可以输)。
最终这个图的最大流就是在Strong Kings个数为ans的情况下能够进行的比赛场数(所有人的分数之和),显然比赛场数是n*(n-1)场,若最大流不等于n*(n-1),那么就表明Strong Kings个数为ans的情况是不合法的。

代码

#include <sstream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXV 1000
#define MAXE 3000
#define MAXN 1000
#define INF 0x3f3f3f3f

using namespace std;

int S,T;

struct edge
{
    int u,v,cap,next;
}edges[MAXE];

int head[MAXV],nCount=1;

void AddEdge(int U,int V,int C)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].cap=C;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

void add(int U,int V,int C)
{
    AddEdge(U,V,C);
    AddEdge(V,U,0);
}

int layer[MAXV],q[MAXE*2];

bool CountLayer()
{
    memset(layer,-1,sizeof(layer));
    int h=0,t=1;
    q[h]=S;
    layer[S]=1;
    while(h<t)
    {
        int u=q[h++];
        for(int p=head[u];p!=-1;p=edges[p].next)
        {
            int v=edges[p].v;
            if(layer[v]==-1&&edges[p].cap)
            {
                layer[v]=layer[u]+1;
                q[t++]=v;
            }
        }
    }
    return layer[T]!=-1;
}

int DFS(int u,int flow)
{
    if(u==T) return flow;
    int used=0;
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(layer[v]==layer[u]+1&&edges[p].cap)
        {
            int tmp=DFS(v,min(flow-used,edges[p].cap));
            used+=tmp;
            edges[p].cap-=tmp;
            edges[p^1].cap+=tmp;
            if(used==flow) return used;
        }
    }
    if(!used) layer[u]=-1;
    return used;
}

int Dinic()
{
    int maxflow=0;
    while(CountLayer())
        maxflow+=DFS(S,INF);
    return maxflow;
}

int n=0,tot=0;
int match[MAXN][MAXN],score[MAXN];
bool matched[MAXN*MAXN];
char s[MAXN];

bool check(int x) //已知有x个Strong Kings
{
    S=MAXV-2,T=MAXV-1;
    nCount=1;
    memset(head,-1,sizeof(head));
    memset(matched,false,sizeof(matched));
    for(int i=1;i<=n;i++)
        add(S,i,score[i]);
    for(int i=1;i<=tot;i++)
        add(i+n,T,1);
    for(int i=n-x+1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            if(score[j]>score[i])
            {
                matched[match[i][j]]=true;
                add(i,match[i][j]+n,1);
            }
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            if(!matched[match[i][j]])
            {
                add(i,match[i][j]+n,1);
                add(j,match[i][j]+n,1);
            }
    return Dinic()==(n*(n-1)/2);
}

int main()
{
    int TestCase;
    scanf("%d",&TestCase);
    getchar(); //!!!!
    while(TestCase--)
    {
        memset(head,-1,sizeof(head));
        nCount=1;
        n=0,tot=0;
        int tmp;
        gets(s);
        stringstream str(s);
        while(str>>tmp) score[++n]=tmp;
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                match[i][j]=++tot;
        int ans;
        for(int i=n;i>=1;i--)
            if(check(i))
            {
                ans=i;
                break;
            }
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值