码迷,mamicode.com
首页 > 其他好文 > 详细

hdoj 4322 Candy 【最大费用最大流】【经典题目】【最大流时 维护费用的最大效益】

时间:2015-08-31 11:53:30      阅读:244      评论:0      收藏:0      [点我收藏+]

标签:


Candy

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1829    Accepted Submission(s): 500


Problem Description
There are N candies and M kids, the teacher will give this N candies to the M kids. The i-th kids for the j-th candy has a preference for like[i][j], if he like the sugar, like[i][j] = 1, otherwise like[i][j] = 0. If the i-th kids get the candy which he like he will get K glad value. If he or she do not like it. He will get only one glad value. We know that the i-th kids will feel happy if he the sum of glad values is equal or greater than B[i]. Can you tell me whether reasonable allocate this N candies, make every kid feel happy.
 

Input
The Input consists of several cases .The first line contains a single integer t .the number of test cases.
For each case starts with a line containing three integers N, M, K (1<=N<=13, 1<=M<=13, 2<=K<=10)
The next line contains M numbers which is B[i](0<=B[i]<=1000). Separated by a single space.
Then there are M*N like[i][j] , if the i-th kids like the j-th sugar like[i][j]=1 ,or like[i][j]=0.
 

Output
If there have a reasonable allocate make every kid feel happy, output "YES", or "NO".
 

Sample Input
2 3 2 2 2 2 0 0 0 0 0 1 3 2 2 2 2 0 0 0 0 0 0
 

Sample Output
Case #1: YES Case #2: NO
Hint
Give the first and second candy to the first kid. Give the third candy to the second kid. This allocate make all kids happy.
 



题意:给N个孩子分配M个糖果。有一个N*M的矩阵表示孩子和糖果的关系,若第i行第j列的数是1则表示第i个孩子喜欢第j个糖果,反之不喜欢。已知,若一个孩子被分配到他喜欢的糖果那么他将获得K的快乐值,反之只能获取1的快乐值。现在给你这N个孩子需要满足的快乐值,问你能不能满足所有孩子的需求


好经典的建图,无限ORZ大牛。


大牛题解:

由于只要得到糖就肯定有1个快乐度,在这一点上糖的效果是等效的。所以只要考虑有特殊效果的糖的分配就可以了。
当快乐的程度超过b[i]时,多出来的部分就浪费了,为了使浪费尽可能少,我们用费用流加以控制,当获得最大费用最大流的时候,这是的费用的利用率就是最高的。在建图时只需考虑特殊的糖就可以了,建图方法:

源点到每个糖:流为1,费用为0。如果糖对某个人有特殊效果,连边:流为1,费用为0。人向汇点连边:最终快乐的程度不超过b[i]的情况:流为b[i]/k,限制这样的糖的数量,费用为k,因为特殊效果全部利用上了。快乐程度超过b[i]的情况:流为1,限制流量不超过b[i],费用为b[i] % k,因为多出来的快乐无效。当b[i] % k == 0时,这样的边没有必要。当b[i] % k == 1时,这时的糖和普通的糖无异,没必要连边(其实我感觉这里的说法与处理不是很恰当,虽然不影响结果)


理理思路,说下自己整理后的建图,和大牛的有一点点不同。


建图:设置超级源点source,超级汇点sink,用a表示当前孩子需要满足的快乐值

1,source向每个糖果建边,容量1,费用0;

2,只考虑有特殊的糖,那么对于第i个人喜欢j糖果,则j糖果向第i个人建边,容量为1,费用为0;

3,每个孩子向汇点建边。这时需要分类讨论 【注意a / K 全是整数之间的运算,一定要自己手算】

     (1) a % K == 0,表示该孩子选择(a / K)个他喜欢的糖果就可以满足,而且该孩子选择1个他喜欢的糖果,会获取K的快乐值。 建边信息——容量为(a / K),费用为K;

     (2) a % K != 0,表示该孩子选择(a / K + 1)个他喜欢的糖果才能满足,这时我们不能只建一条容量为(a / K + 1),费用为K的边,如果这样处理我们就放大了最大流时费用的最大效益,最后得到的快乐值为a - a % K +  K > a。因此我们要建一条容量为(a / K),费用为K的边,再建一条容量为1,费用为a % K的边。这样的话在用特殊糖果满足该孩子的需求时,才不会使最后流入汇点的费用增加,最后得到的快乐值为a - a % K + a % K = a。


建好图,跑一次最大费用最大流。

最终结果:(用sumflow记录所有孩子需要满足的快乐值之和)

最大费用cost——特殊的糖被充分利用后所分配的快乐值之和。若cost >= sumflow 则表示已经满足条件。

最大流flow——为了达到这样的程度使用的糖的数量。

这样就还剩M - flow数目的糖被我们当做普通的糖使用,只要M - flow >= sumflow - cost,就可以满足条件。


AC代码:


#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN 50
#define MAXM 5000
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, cost, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int pre[MAXN], dist[MAXN];
bool vis[MAXN];
int N, M, K;
int source, sink;
int sumflow;
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
    Edge E1 = {u, v, w, 0, c, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, -c, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
void getMap()
{
    source = 0, sink = N + M + 1;
    int a;
    sumflow = 0;
    for(int i = 1; i <= N; i++)
    {
        scanf("%d", &a);
        sumflow += a;
        addEdge(i, sink, a / K, K);//可以选择a / K个他喜欢的糖果
        if(a % K > 1)//保证不会有多余的费用流进入汇点  维护最大流时 费用的最大效益
            addEdge(i, sink, 1, a % K);
    }
    for(int i = 1; i <= M; i++)//源点从 每个糖果
        addEdge(source, i+N, 1, 0);
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= M; j++)
        {
            scanf("%d", &a);
            if(a)//根据喜欢 的关系 建边
                addEdge(j+N, i, 1, 0);
        }
    }
}
bool SPFA(int s, int t)
{
    queue<int> Q;
    memset(dist, -INF, sizeof(dist));
    memset(vis, false, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(dist[E.to] < dist[u] + E.cost && E.cap > E.flow)
            {
                dist[E.to] = dist[u] + E.cost;
                pre[E.to] = i;
                if(!vis[E.to])
                {
                    vis[E.to] = true;
                    Q.push(E.to);
                }
            }
        }
    }
    return pre[t] != -1;
}
void MCMF(int s, int t, int &cost, int &flow)
{
    cost = flow = 0;
    while(SPFA(s, t))
    {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            Edge E = edge[i];
            Min = min(Min, E.cap-E.flow);
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
}
int main()
{
    int t, k = 1;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d%d", &M, &N, &K);
        init();
        getMap();
        int cost, flow;
        MCMF(source, sink, cost, flow);
        //跑出的流量是使用特殊糖果的数目
        //最大费用 是使用这些糖果得到的最大快乐值
        printf("Case #%d: ", k++);
        if(cost >= sumflow || sumflow - cost <= M - flow)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

hdoj 4322 Candy 【最大费用最大流】【经典题目】【最大流时 维护费用的最大效益】

标签:

原文地址:http://blog.csdn.net/chenzhenyu123456/article/details/48130365

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!