T79167 新田忌赛马

Posted -hhs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了T79167 新田忌赛马相关的知识,希望对你有一定的参考价值。

题目背景

田忌又要和齐王赛马了,这一次赛的不止三匹马,田忌请学OI的你帮忙

题目描述

田忌和齐王各有n匹马(1<=n<=1000),他们的编号分别是1-n,给出田忌每条马能胜过齐王的哪些马(非赢则输,不存在平局的情况),每匹马最多出战一次,可以自由安排交战顺序,问田忌最多可以赢多少局。

输入输出格式

输入格式:

一行一个数n, 表示齐王和田忌各自马的数量 下面n行 第i行第一个数c,表示田忌的第i匹马能赢得马的数量,这一行后面有c个数,表示第i匹马能赢得对方的马的编号

输出格式:

一个数ans,表示田忌的最大胜场数

输入样例1:

4
3 2 1 3 
1 2 
2 2 1 
1 3 

输出样例1:

3

输入样例2:

8
1 6 
4 5 1 4 2 
4 3 1 6 5 
1 5 
1 2 
2 4 5 
6 3 1 4 2 7 6 
4 3 7 6 2 

输出样例2:

7

说明

对于20%的数据 n<=20 对于50%的数据 n<=200 对于100%的数据 n<=2000 c<=n

 

思路:

  本题是二分图最大匹配:

  1、匈牙利算法(只能拿90分, 被一个无良的出题人卡掉一个点):

     核心思想:寻找增广路径,用增广路径求二分图最大匹配。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>

using namespace std;

int n,a,b;
int line[2008][2008],use[2008],horse[10008];
int ans=0;

long long read()

    long long x=0,f=1;
    char ch=getchar();
    while(ch<0||ch>9)if(ch==-)f=-1;ch=getchar();
    while(ch>=0&&ch<=9)x=x*10+ch-0;ch=getchar();
    return x*f;


int find(int x)//寻找增广路径 

    for(int i=1;i<=n;++i)//扫描右图每一个点 
    
        if(line[x][i]&&!use[i])//如果x与i可以匹配,并且i没有被拆开尝试匹配并失败 
        
            use[i]=1;//标记i在此轮中被拆开尝试匹配 
            if(horse[i]==0||find(horse[i]))//如果i没有被匹配过,或者现在i的匹配对象可以匹配到别人 
            
                horse[i]=x;
                return 1;
            
        
    
    return 0;


int main()

    n=read();
    for(int i=1;i<=n;++i)
    
        a=read();
        for(int j=1;j<=a;++j)
        
            b=read();
            line[i][b]=1;
        
    
    for(int i=1;i<=n;++i)
    
        memset(use,0,sizeof(use));
        if(find(i)) ans++;//真就能找到一条增广路径,假则否 
    
    printf("%d",ans);
    return 0;

  2、网络流:

    其实所有的二分图最大匹配问题都可以用网络流来做,并且比匈牙利算法更优。若对网络流有疑问可以看一下算法篇里的网络流。

    如样例1的数据,图应该是这样的。其中,S\\T分别为超级源点\\汇点。

  技术图片

  

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<iostream>

const int inf=0x7fffffff;

using namespace std;

int n,m,S,T;
int u,v,w;
int head[10008],num_edge=0;
int deep[10008];

struct Edge
    int next,to,dis;
edge[100008<<1];

queue <int> q;

long long read()

    long long x=0,f=1;
    char ch=getchar();
    while(ch<0||ch>9)if(ch==-)f=-1;ch=getchar();
    while(ch>=0&&ch<=9)x=x*10+ch-0;ch=getchar();
    return x*f;


void addedge(int from,int to,int dis)

    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    head[from]=num_edge;


bool bfs()

    memset(deep,0,sizeof(deep));
    while(!q.empty()) q.pop();
    deep[S]=1;
    q.push(S);
    do
    
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=edge[i].next)
        
            int v=edge[i].to;
            if(deep[v]==0&&edge[i].dis)
            
                deep[v]=deep[u]+1;
                q.push(v);
            
        
    while(!q.empty());
    if(deep[T]!=0) return 1;
    else return 0;


int dfs(int u,int dist)

    if(u==T) return dist;
    for(int i=head[u];i;i=edge[i].next)
    
        if( deep[edge[i].to]==deep[u]+1 && edge[i].dis!=0 )
        
            int di=dfs(edge[i].to,min(dist,edge[i].dis));
            if(di>0)
            
                edge[i].dis-=di;
                edge[i+1].dis+=di;
                return di;
            
        
    
    return 0;


int xiongyali()

    int ans=0;
    while(bfs())
    
        while(int di=dfs(S,inf))
        
            ans+=di;
        
    
    return ans;


int main()

    n=read();m=n;S=10002;T=10003;
    for(int i=1;i<=n;++i)
    
        int a,b;
        a=read();
        for(int j=1;j<=a;++j)
        
            b=read();
            b+=5000;
            addedge(i,b,1);
            addedge(b,i,0);
        
    
    for(int i=1;i<=n;++i)
    
        addedge(S,i,1);
        addedge(i,S,0);
    
    for(int i=1;i<=n;++i)
    
        int j=i+5000;
        addedge(j,T,1);
        addedge(T,j,0);
    
    printf("%d",xiongyali());
    return 0;

 

以上是关于T79167 新田忌赛马的主要内容,如果未能解决你的问题,请参考以下文章

算法大师孙膑--田忌赛马(转自labuladong)

2181 田忌赛马

田忌赛马

洛谷P1650赛马与codevs 2181 田忌赛马

田忌赛马

田忌赛马