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

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;
}

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

时间: 2024-10-09 20:18:50

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

hdoj 1569 方格取数 【最大点权独立集-&gt;最大流】

题目:hdoj 1569 方格取数 题意:中文题目,就不说题意了. 分类:最大流 |  dp 分析:dp的话应该是个数塔模型,不难做,这里讲转化为图的做法. 这个题目的关键在于转化为一个二分图,来求一个二分图的最大点权独立集,而最大点权独立集 = 点权和 - 最小点权覆盖 最小点权覆盖: 从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小. 最大点权独立集:找到二分图中权值和最大的点集,然后让任意点没有边. 而最小点权覆盖 = 最小割 = 最大流 = sum - 最大

【bzoj3130】[Sdoi2013]费用流 二分+网络流最大流

题目描述 Alice和Bob做游戏,给出一张有向图表示运输网络,Alice先给Bob一种最大流方案,然后Bob在所有边上分配总和等于P的非负费用.Alice希望总费用尽量小,而Bob希望总费用尽量大.求两人都采取最优策略的情况下最大流及总费用. 输入 第一行三个整数N,M,P.N表示给定运输网络中节点的数量,M表示有向边的数量,P的含义见问题描述部分.为了简化问题,我们假设源点S是点1,汇点T是点N.接下来M行,每行三个整数A,B,C,表示有一条从点A到点B的有向边,其最大流量是C. 输出 第一

POJ 2455--Secret Milking Machine【二分枚举 &amp;&amp; 最大流 &amp;&amp; 经典】

Secret Milking Machine Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 10625   Accepted: 3111 Description Farmer John is constructing a new milking machine and wishes to keep it secret as long as possible. He has hidden in it deep within

矩阵经典题目七:Warcraft III 守望者的烦恼(矩阵加速递推)

https://www.vijos.org/p/1067 很容易推出递推式f[n] = f[n-1]+f[n-2]+......+f[n-k]. 构造矩阵的方法:构造一个k*k的矩阵,其中右上角的(k-1)*(k-1)的矩阵是单位矩阵,第k行的每个数分别对应f[n-1],f[n-2],,f[n-k]的系数.然后构造一个k*1的矩阵,它的第i行代表f[i],是经过直接递推得到的.设ans[][]是第一个矩阵的n-k次幂乘上第二个矩阵,f[n]就是ans[k][1]. 注意:用__int64 #in

黑马程序员—IO(一)流概述、字符流、字节流、流操作规律

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 第一节     IO概述 概述 1.IO流:即InputOutput的缩写. 2.特点: 1)IO流用来处理设备间的数据传输. 2)Java对数据的操作是通过流的方式. 3)Java用于操作流的对象都在IO包中. 4)流按其操作数据的类型分为两种:字节流和字符流. 5)流按其流向分为:输入流和输出流. 注意:流只能操作数据,而不能操作文件. 3.IO流的常用基类: 1)字节流的抽象基流:Inp

黑马程序员——IO流概述、字符流、字节流、流操作规律(一)

第一节     IO概述 概述 1.IO流:即InputOutput的缩写. 2.特点: 1)IO流用来处理设备间的数据传输. 2)Java对数据的操作是通过流的方式. 3)Java用于操作流的对象都在IO包中. 4)流按其操作数据的类型分为两种:字节流和字符流. 5)流按其流向分为:输入流和输出流. 注意:流只能操作数据,而不能操作文件. 3.IO流的常用基类: 1)字节流的抽象基流:InputStream和OutputStream 2)字符流的抽象基流:Reader和Writer 注:此四个

Java I/O流操作(二)---缓冲流[转]

转自:http://blog.csdn.net/johnny901114/article/details/8710403 一.BufferWriter类 IO的缓冲区的存在就是为了提高效率,把要操作的数据放进缓冲区,然后一次性把缓冲区的内容写到目的地,而不是写一次就往目的地写一次. 在这里要注意的是当我们关闭了缓冲区对象实际也关闭了与缓冲区关联的流对象. BufferWriter类 try { FileWriter fw =new FileWriter("test.txt"); //使

Java核心类库-IO-包装流概述和缓冲流原理

处理流/包装流(相对于节点流更高级): 1. 隐藏了底层的节点流的差异,并对外提供了更方便的输入/输出功能,让我们之关心高级流操作. 2. 使用处理包装流包装了节点流程序直接操作处理流,让节点流与底层的设备做IO操作. 3. 只需要关闭处理流即可 包装流如何分区:写代码的时候,发现创建对象的时候,需要传递另一个流对象.new 包装流(流对象): 什么是缓冲流: 是一个包装流,目的起缓存作用. BufferedInputStream: BufferedOutputStream: BufferedR

学习心得:《十个利用矩阵乘法解决的经典题目》from Matrix67

本文来自:http://www.matrix67.com/blog/archives/tag/poj大牛的博文学习学习 节选如下部分:矩阵乘法的两个重要性质:一,矩阵乘法不满足交换律:二,矩阵乘法满足结合律经典题目1 给定n个点,m个操作,构造O(m+n)的算法输出m个操作后各点的位置.操作有平移.缩放.翻转和旋转    这 里的操作是对所有点同时进行的.其中翻转是以坐标轴为对称轴进行翻转(两种情况),旋转则以原点为中心.如果对每个点分别进行模拟,那么m个操作总共耗时 O(mn).利用矩阵乘法可