【bzoj1305】【CQOI2009】【dance】【跳舞】

题目大意

有n个男孩和n个女孩,男女间要么互相喜欢要么不喜欢,跳一次舞每个人和没跳过的喜欢的跳或和最多k个不喜欢的跳。求最多跳几次。

解题思路

有一个直观的贪心,最大的答案为每个人喜欢的人数取最小值后加k,但我们能不能保证有合法解呢,反正bzoj上能过,但某个oj上不能过。

于是就打了网络流,先二分一个答案,每个人拆成两个点(和喜欢的人跳与和不喜欢的人跳),源到和喜欢的人跳流量为ans,和喜欢的人跳到和不喜欢的人跳流量为k,每个男女流量为1。

code

贪心(正确性可能有问题)

#include
#include
#include
#include
#include
#define LF double
#define LL long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a>b)?b:a)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=50000;
int n,K,a[maxn+10],b[maxn+10];
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d%d\n",&n,&K);
    fo(i,1,n){
        fo(j,1,n){
            char ch;scanf("%c",&ch);
            if(ch=='Y')a[i]++,b[j]++;
        }
        scanf("\n");
    }
    int ans=n;
    fo(i,1,n)ans=min(ans,min(a[i]+K,b[i]+K));
    printf("%d",ans);
    return 0;
}

正解

#include
#include
#include
#include
#include
#define LF double
#define LL long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a>b)?b:a)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=50,inf=2147483647;
int n,K,gra,to[maxn*8+maxn*maxn*2+10],size[maxn*8+maxn*maxn*2+10],cost[maxn*8+maxn*maxn*2+10],next[maxn*8+maxn*maxn*2+10],begin[maxn*4+10],dis[maxn*4+10];
bool map[maxn+10][maxn+10],done[maxn*4+10];
void insert(int u,int v,int s){
    to[gra]=v;
    size[gra]=s;
    cost[gra]=1;
    next[gra]=begin[u];
    begin[u]=gra++;
    to[gra]=u;
    size[gra]=0;
    cost[gra]=-1;
    next[gra]=begin[v];
    begin[v]=gra++;
}
int addflow(int now,int t,int flow){
    done[now]=1;
    if(now==t)return flow;
    for(int i=begin[now],add;i;i=next[i])
        if(size[i]&&(!done[to[i]])&&(dis[now]==dis[to[i]]+cost[i])&&(add=addflow(to[i],t,min(flow,size[i])))){
            size[i]-=add;
            size[i^1]+=add;
            return add;
        }
    return 0;
}
bool updis(int s,int t){
    int add=inf;
    fo(i,s,t)
        if(done[i])
            for(int j=begin[i];j;j=next[j])
                if(size[j]&&(!done[to[j]]))
                    add=min(add,dis[to[j]]+cost[j]-dis[i]);
    if(add==inf)return 0;
    fo(i,s,t)
        if(done[i])dis[i]+=add;
    return 1;
}
int maxflow(int s,int t){
    int ans=0;
    do{
        memset(done,0,sizeof(done));
        for(int add;add=addflow(s,t,inf);){
            ans+=add;
            memset(done,0,sizeof(done));
        }
    }while(updis(s,t));
    return ans;
}
bool check(int ans){
    gra=2;memset(begin,0,sizeof(begin));
    fo(i,1,n)insert(0,i,ans),insert(i,i+n,K),insert(i+n*2,i+n*3,K),insert(i+n*3,n*4+1,ans);
    fo(i,1,n)
        fo(j,1,n)
            if(map[i][j])insert(i,j+n*3,1);
            else insert(i+n,j+n*2,1);
    return ans*n==maxflow(0,n*4+1);
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d%d\n",&n,&K);
    fo(i,1,n){
        fo(j,1,n){
            char ch;scanf("%c",&ch);
            if(ch=='Y')map[i][j]=1;
        }
        scanf("\n");
    }
    int l=0,r=n;
    for(;l!=r;){
        int m=(l+r+1)/2;
        if(check(m))l=m;
        else r=m-1;
    }
    printf("%d",l);
    return 0;
}

你可能感兴趣的:(网络流,bzoj,二分,三分)