BZOJ-1305 dance跳舞 建圖+最大流+二分判定

weixin_30639719發表於2020-04-05
跟隨YveH的腳步又做了道網路流。。。%%%

1305: [CQOI2009]dance跳舞
Time Limit: 5 Sec Memory Limit: 162 MB
Submit: 2119 Solved: 878
[Submit][Status][Discuss]

Description
一次舞會有n個男孩和n個女孩。每首曲子開始時,所有男孩和女孩恰好配成n對跳交誼舞。每個男孩都不會和同一個女孩跳兩首(或更多)舞曲。有一些男孩女孩相互喜歡,而其他相互不喜歡(不會“單向喜歡”)。每個男孩最多隻願意和k個不喜歡的女孩跳舞,而每個女孩也最多隻願意和k個不喜歡的男孩跳舞。給出每對男孩女孩是否相互喜歡的資訊,舞會最多能有幾首舞曲?

Input
第一行包含兩個整數n和k。以下n行每行包含n個字元,其中第i行第j個字元為’Y’當且僅當男孩i和女孩j相互喜歡。

Output
僅一個數,即舞曲數目的最大值。

Sample Input
3 0
YYY
YYY
YYY

Sample Output
3

HINT
N<=50 K<=30

Source
加強資料By dwellings and liyizhen2

這道題,網上有說可以貪心的,然而似乎被證明是錯誤的了,(反正我也想不出貪心策略)
說一下建圖:(建圖有點麻煩,多謝恆妹的查錯)
每個男孩建立兩個次節點,每個女孩建立兩個次節點,分別處理喜歡的和不喜歡的
超級源S向所有男生主節點連邊,所有女生向超級匯連邊,邊權為二分列舉出來的值
男生主節點和處理喜歡的男生次節點連邊邊權為INF,向處理不喜歡的男生連邊邊權為k
女生處理喜歡的次節點向主節點連邊,邊權INF;處理不喜歡的次節點與主節點相連邊權為k
相互喜歡的男生女生的喜歡節點相連邊權為1,相互不喜歡的連邊,邊權為1
二分答案,及超級源指向男生主節點的邊權(女生主節點指向超級匯的邊權)
每次二分出答案重新建圖,跑一邊Dinic判斷是否滿流,最後輸出二分出邊權即可。

(這道題目不錯不錯)
(一開始沒寫二分寫了個列舉,結果卡常了。。。“竟然被卡常了,666啊”—by YveH)

code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int q[3000],h,t;
struct data{
    int to,next,v;
}edge[1000001];
int cnt;
int head[3000]={0};
int dis[3000]={0};
int xh[51][51];
int n,k,num;

void add(int u,int v,int w)
{
    cnt++;
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
    edge[cnt].v=w;
}

void init()
{
    scanf("%d%d",&n,&k);
    for (int i=1; i<=n; i++)
        {
            char x[51];
            scanf("%s",&x);
            for (int j=1; j<=n; j++)
                if (x[j-1]=='Y')
                    xh[i][j]=1;
        }
}

void make(int v)
{
    //男生i的主點序號為 in 則處理喜歡的次點序號為in+1,處理不喜歡的次點序號為in+2
    //女生同理,接男生後面排 
    //超級源的序號0,超級匯為2*n*3+1 
    cnt=1; memset(head,0,sizeof(head));
    num=1;
    for (int i=1; i<=n; i++)
        {
            add(0,num,v);add(num,0,0);
            num++;
            add(num-1,num,0x7fffffff);add(num,num-1,0);
            num++;
            add(num-2,num,k);add(num,num-2,0);
            num++;  
        }
    for (int i=1; i<=n; i++)
        {
            add(num,2*n*3+1,v);add(2*n*3+1,num,0);
            num++;
            add(num,num-1,0x7fffffff);add(num-1,num,0);
            num++;
            add(num,num-2,k);add(num-2,num,0);
            num++;
        }
    for (int i=1; i<=n; i++)
        {
            for (int j=1; j<=n; j++)
                {
                    if (xh[i][j]==1)
                        {
                            add(i*3-2+1,3*n+j*3-2+1,1);
                            add(3*n+j*3-2+1,i*3-2+1,0);
                        }
                    else
                        {
                            add(i*3-2+2,3*n+j*3-2+2,1);
                            add(3*n+j*3-2+2,i*3-2+2,0);                         
                        }
                }
        }
}

bool bfs()
{
    memset(dis,-1,sizeof(dis));
    q[1]=0; dis[0]=1;
    h=0; t=1;
    while (h<t)
        {
            int j=q[++h],i=head[j];
            while (i)
                {
                    if (edge[i].v>0 && dis[edge[i].to]<0)
                        {
                            dis[edge[i].to]=dis[j]+1;
                            q[++t]=edge[i].to;
                        }
                    i=edge[i].next;
                }
        }
    if (dis[num]>0)
        return true;
    else
        return false;
}

int dfs(int loc,int low)
{
    int now=0;
    if (loc==num)   return low;
    int i=head[loc];
    while (i)
        {
            if (edge[i].v>0 && dis[edge[i].to]==dis[loc]+1 && (now=dfs(edge[i].to,min(edge[i].v,low))))
                {
                    edge[i].v-=now;
                    edge[i^1].v+=now;
                    return now;
                }
            i=edge[i].next;
        }
    return 0;
}

int main()
{
    init();
    int ans;
    int flow;
    int l=0,r=50;
    while (l<=r)
        {
            int mid=(l+r)>>1;
            make(mid);
            flow=0;
            while (bfs())
                {
                    int now=0;
                    while ((now=dfs(0,0x7fffffff)))
                    flow+=now;
                }
            if (mid*n<=flow)
                {l=mid+1;ans=mid;}
            else
                r=mid-1;
        }//二分判斷是否滿流 
    printf("%d",ans);
    return 0;
}

轉載於:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5346233.html

相關文章