BZOJ1532/POI2005 Dicing

直接求似乎不简单,由于题目求”最大值最小问题”,可以转化为二分答案来验证一个解是否成立.假设枚举的答案是x,那么所有人赢得的场次都<=x.而每场比赛最多只有一个人获胜,我们可以根据此信息,构图:
设置源点与每个人建边,每个人与它所参加的每场比赛建边,每场比赛与超级汇点建边并且流量设置为1.根据此图跑一遍最大流,判断是否为m即可.

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=10005;
const int oo=1e9+5;
int head[M<<1],n,m,u[M],v[M],ec=0,tot;
int dis[M<<1],flw[M<<1];// dis表示层数 
struct node{
    int to,cap,nex;
}e[M*8];//边数 (n+3*m)*2 
int Q[M<<2];
inline void rd(int &res){
    res=0;char c;
    while(c=getchar(),c<48);
    do res=(res<<1)+(res<<3)+(c^48);
    while(c=getchar(),c>=48);
}
void ins(int a,int b,int c){
    e[ec]=(node){b,c,head[a]};
    head[a]=ec++;
    e[ec]=(node){a,0,head[b]};
    head[b]=ec++;
}
bool BFS(){
    memset(dis,-1,sizeof(dis));
    int L=0,R=0,i,j,k;
    dis[0]=0;
    Q[R++]=0;
    while(L<R){
        int x=Q[L++];
        if(x==tot)return true;
        for(i=head[x];~i;i=e[i].nex){
            int to=e[i].to,cap=e[i].cap;
            if(dis[to]==-1&&cap){
                dis[to]=dis[x]+1;
                Q[R++]=to;
            }
        }
    }
    return false;
}
int dfs(int x,int flw){
    if(x==tot)return flw;
    int now=0;
    for(int i=head[x];~i;i=e[i].nex){
        int to=e[i].to,cap=e[i].cap;
        if(dis[to]!=dis[x]+1||!cap)continue;
        int f=dfs(to,min(flw,cap));
        now+=f;
        e[i].cap-=f;
        e[i^1].cap+=f;
        if(now==flw)break;
    }
    return now;
}
bool chk(int res){
    memset(head,-1,sizeof(head));//初始化
    ec=0;
    int ans=0,i,j,k,a,b;
    for(i=1;i<=n;i++)ins(0,i,res);
    for(i=1;i<=m;i++){
        ins(u[i],i+n,res);
        ins(v[i],i+n,res);
        ins(i+n,tot,1);
    }//构图
    while(BFS()){
        do a=dfs(0,oo),ans+=a;
        while(a!=0);
    }
    if(ans==m)return true;
    return false;
}
int main(){
    int l,r,i,j,k,ans;
    rd(n);rd(m);tot=n+m+1;
    for(i=1;i<=m;i++){
        rd(v[i]);rd(u[i]);
    }
    l=(m+n-1)/n;r=m;
    while(l<=r){
        int mid=l+r>>1;
        if(chk(mid)){
            ans=mid;r=mid-1;
        }else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

展开阅读全文
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值