hdu4322 candy 費用流

題意:

n個糖果,m個孩子,給一個矩陣like[i][j]表示第i個孩子喜歡第j個糖果。 如果孩子拿到他喜歡的糖果,那麼他將會增加k個快樂度,拿到不喜歡的,增加1。 如果孩子i的歡樂值大於B[i],那麼他就是開心的。

  問,能否有一種分配方案,讓所有孩子都開心,有輸出yes,沒有no。

思路:(講的不好請見諒,大牛勿噴)

      起初是看了一個大牛的一篇關於網絡流建模的總結,裏面有個跟這個題同名的題,只是它裏面k是固定的就是2.裏面的做法是用單純的最大流做的,沒有考慮到費用。建圖是這樣的,每顆糖作爲一個點連邊(s,i,1),每個小孩j座位一個點連邊(j,t,b[i]/2),若小孩j喜歡糖果i則連邊(i,j,1),然後最大流一下,得ans,,ans+n>=求和(b[i]).他的考慮是反正每顆糖最後都是要分給孩子的,那麼它一定會爲總權值貢獻1,所以這個1就不考慮了,最後加上n,只考慮如果孩子喜歡這個糖而帶來的額外的貢獻,上述題裏面這樣做是沒問題的。其實這兩道題的思路很像,hdu4322應該是改變自上述題的,將k變成了變化的,而不是2了。

     一開始我拿到這個題的時候,就直接套了上述題的思路,想將小孩j作爲一個點練邊(j,t,b[i]/k),表示該孩子能用b[i]/k塊糖果填補一下快樂度,走一邊最大流,然後用剩下的n-ans塊糖來填補這些孩子不夠的快樂度。後來一想這樣做是不對的,比如k=3,b[i]%k==2,如果套上述建圖方法,也就是說我需要用兩塊糖來填補這個孩子的快樂度,並且這兩塊糖裏面可能會有他喜歡的糖果,因爲走最大流的時候只讓它滿足了b[i]/k個流量。但這時你給這個孩子一個他喜歡的糖果,快樂度會超出1,但是糖果使用上是更少了,這是更合理的。所以只用最大流是錯的。這時候就想到了需要用到費用流。

     其中的總費用代表了給這些孩子喜歡的糖果所能得到的總貢獻,每給一個孩子一塊它喜歡的糖果,其費用是k,而且每個孩子的快樂度到達b[i]即可,就算超過了也按b[i]算,因爲超過的部分沒有意義。然後呢,詳細考慮一下,如果一個孩子b[i]%k==0 那麼建邊(j,t,b[i]\k,k),容量是b[i]\k,費用是k,那麼如果b[i]%k!=0呢?

如果==1,也就是說剩餘的快樂度就算你給他喜歡的糖果效果也跟普通糖果一樣,這時候就不需要建邊了。如果>1,這時候就需要再建第二條邊(j,t,1,b[i]%k),容量是1,費用是b[i]%k,它的意義就是用一個喜歡的糖果把b[i]%k的部分填補掉。但這時候再考慮一個問題,就是如果你套最小費用最大流的板子,它肯定會先走第二條邊,但這樣是不對的。這樣的話就有可能導致費用是k的邊的容量有剩餘時,而第二條邊已經被流了,道理自己想吧。所以我們需要做的就是先讓它流費用大的,所以這是個最大費用最大流。只需要把費用取相反數,流完再取反就好了。

    最後判斷n-ans>=all(b[i])-cost是否成立就好了,也就是剩餘的沒人喜歡(或者被某些孩子喜歡但這個孩子快樂度夠了而不能要)的糖果是不是能滿足剩餘的快樂度。

代碼:最小費用流的板子屯一發

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
const int inf=1<<29;
const int maxn=1000;

struct node
{
    int v,next,cap,w;
};
node edge[maxn<<1];
int head[maxn];
int cnt;
int dis[maxn],pe[maxn],pv[maxn];
int m,n,k;
int cost,flow;
int S,T,all;
bool mark[maxn];

void init()
{
    memset(head,-1,sizeof(head));
    cnt=0;
    all=0;
}

void add(int a,int b,int c,int d)
{
    edge[cnt].v=b;
    edge[cnt].cap=c;
    edge[cnt].w=d;
    edge[cnt].next=head[a];
    head[a]=cnt++;

    edge[cnt].v=a;
    edge[cnt].cap=0;
    edge[cnt].w=-d;
    edge[cnt].next=head[b];
    head[b]=cnt++;
}

int mindinic()
{
    int Min;
    cost=0,flow=0;
    while(1)
    {
        for(int i=0;i<=T;i++) dis[i]=inf;
        memset(mark,false,sizeof(mark));
        queue<int> q;
        dis[0] = 0;
        q.push(0);
        mark[0]=true;
        int cur;
        while(!q.empty())
        {
            cur = q.front();
            q.pop();
            mark[cur]=false;
            for(int i = head[cur]; i != -1; i = edge[i].next)
            {
                node x=edge[i];
                if(x.cap&& dis[x.v]>x.w+dis[cur] )
                {
                    dis[x.v] = dis[cur] + x.w;
                    pe[x.v]=i;
                    pv[x.v]=cur;
                    if(!mark[x.v])
                    {
                        mark[x.v]=true;
                        q.push(x.v);
                    }
                }
            }
        }
        if(dis[T]==inf) return cost;
        Min=inf;
        for(int j=T; j!=S; j=pv[j])
            Min=min(Min,edge[pe[j]].cap);
        flow+=Min;
        cost+=dis[T]*Min;
        for(int j=T; j!=S; j=pv[j])
            edge[pe[j]].cap-=Min,edge[pe[j]^1].cap+=Min;
    }
}

int main()
{
    int cas=1;
    int t,x;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&k);
        init();
        S=0,T=n+m+1;
        for(int i=1; i<=n; i++)
        {
            add(S,i,1,0);
        }
        for(int i=1; i<=m; i++)
        {
            scanf("%d",&x);
            all+=x;
            add(n+i,T,x/k,-k);
            if(x%k>1){
                add(n+i,T,1,-x%k);
            }
        }
        for(int i=1; i<=m; i++)
        {
            for(int j=1; j<=n; j++)
            {
                scanf("%d",&x);
                if(x)
                {
                    add(j,n+i,1,0);
                }
            }
        }
        int tmp=-mindinic();//用上的喜歡的糖果能夠填充的快樂度
        if(n-flow>=all-tmp)//剩餘的沒人喜歡的糖果是否能填充剩餘的快樂度
            printf("Case #%d: YES\n",cas++);
        else
            printf("Case #%d: NO\n",cas++);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章