[玲珑杯] Problem E: Can Win [匹配->最大流][Dinic]

Description

Zhc很喜欢看某个竞技比赛,比赛的规则是这样的:队伍分成AB两组进行比赛,除了组内比赛,两组之间还会进行一定的比赛,每场比赛赢者得1分,输者不得分,没有平局的情况。 在A组里面Zhc有一支自己非常喜欢的队伍,现在比赛已经进行到一半了,Zhc想知道,他支持的那支队伍有没有可能获得最终的胜利(A组最高分即为胜利,允许多支队伍同时最高分)

Input

第一行输入样例组数T<=110

每组样例第一行输入A组队伍数量n<=400,以及Zhc支持的队伍编号(1-n)K<=n

第二行按编号输入A组各队伍目前的成绩Mark[i]<= 300000

第三行按编号输入A组各队伍剩余比赛总场数Cnt[i]<= 300000

下面有一个N*N的矩阵,其中A[i][j]代表编号i的队伍跟编号j的队伍剩余比赛场数A[i][j]<=100

Output

对每组样例输出”Yes”或者”No”(不用输出引号)代表Zhc支持的队伍能获得最终的胜利,换行处理

Sample Input

1
2 1
5 6
2 2
0 1
1 0
Sample Output

Yes

题目链接

题解

匹配问题通常是转化成网络流问题解决的

下面是官方题解
转化为网络流模型:

这里写图片描述

其中MAX 为第k 队的最高得分,即mark[k]+cnrt[k]。
对于每一个a[i][j],按左半部分建边;
对于每一个点i,向汇点建立一条MAX-mark[i]的边。
从s 向t 跑最大流,如果从s 连向i,j 的边都满流,则第k 个队可以获得胜利。

下面是根据官方题解敲的代码 122ms

#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<algorithm>
#include<vector>
#define MAX_N 405
#define INF 0x3f3f3f3f
using namespace std;

const int MAXS=10*1024*1024;
char buf[MAXS],*ch;

void read(int &x){
    while(*(++ch)<'0');
    for(x=0;*ch>='0';++ch) x=x*10+*ch-'0';
}

void read_init(){
    ch=buf-1;
    fread(buf,1,MAXS,stdin);
}

/**************************** HEAD *****************************/

struct edge{int to,cap,rev,next;};
int pit[MAX_N*MAX_N];
edge dat[MAX_N*MAX_N<<2];

//vector<edge> G[MAX_N*MAX_N];
int lever[MAX_N*MAX_N];
int iter[MAX_N*MAX_N];

int u;
void add_edge(int x,int y,int cap){
    dat[u].next=pit[x];
    dat[u].to=y;
    dat[u].cap=cap;
    dat[u].rev=u+1;
    pit[x]=u++;

    dat[u].next=pit[y];
    dat[u].to=x;
    dat[u].cap=0;
    dat[u].rev=u-1;
    pit[y]=u++;
//  G[x].push_back((edge){y,cap,G[y].size()});
//  G[y].push_back((edge){x,0,G[x].size()-1});
}

void bfs(int s){
    memset(lever,-1,sizeof(lever));
    lever[s]=0;
    queue<int> que;
    que.push(s);
    while(!que.empty()){
        int v=que.front();que.pop();
        for(int i=pit[v];i;i=dat[i].next){
            edge &e=dat[i];
            if(lever[e.to]==-1&&e.cap>0){
                lever[e.to]=lever[v]+1;
                que.push(e.to);
            }
        }
    }
}

int dfs(int v,int t,int f){
    if(v==t) return f;
    for(int &i=iter[v];i;i=dat[i].next){
        edge &e=dat[i];
        if(e.cap>0&&lever[v]<lever[e.to]){
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0){
                e.cap-=d;
                dat[e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}

int max_flow(int s,int t){
    int f,flow=0;
    while(true){
        bfs(s);
        if(lever[t]==-1) return flow;
        for(int i=0;i<=t;i++) iter[i]=pit[i];
        while(f=dfs(s,t,INF)) flow+=f;
    }
}

int Mark[MAX_N];
int Cnt[MAX_N];
int A[MAX_N][MAX_N];

int main()
{
    read_init();
    int T;read(T);
    while(T--){
        memset(pit,0,sizeof(pit));u=1;
        int n,k;read(n);read(k);
        for(int i=1;i<=n;i++) read(Mark[i]);
        for(int i=1;i<=n;i++) read(Cnt[i]);
        for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) read(A[i][j]);

        int MAX=Mark[k]+Cnt[k];
        for(int i=1;i<=n;i++) A[i][k]=A[k][i]=0;

        int s=0,t=n+n*n+1,sum=0;
        bool flag=true;
        for(int i=1;flag&&i<=n;i++){
            if(i==k) continue;
            if(MAX-Mark[i]>=0)
                add_edge(i,t,MAX-Mark[i]);
            else
                flag=false;
        }
        if(!flag){
            puts("No");
            continue;
        }
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                if(A[i][j]){
                    add_edge(s,i*n+j,A[i][j]);
                    add_edge(i*n+j,i,A[i][j]);
                    add_edge(i*n+j,j,A[i][j]);
                    sum+=A[i][j];
                }
            }
        }
        int flow=max_flow(s,t);
        puts(flow==sum?"Yes":"No");
    //  for(int i=0;i<=t;i++) while(!G[i].empty()) G[i].pop_back();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值