hdu4322 Candy 最小费用最大流

来源:互联网 发布:批量删除hbase中的数据 编辑:程序博客网 时间:2024/05/16 10:13
/*
        题目描述:有n种糖果和m个小孩,每个小孩有一个要求的愉悦度b[i],给出一个n*m的like矩阵,若like[i][j]=1说明i小孩喜欢j糖果,那么如果
        将j糖果分给i小孩则i小孩将获得K点愉悦度,若like[i][j] = 0,那么如果将i糖果分给j小孩,那么i小孩将获得1点愉悦度,求问有没有糖果的分配方
        案使得每个小孩都满足其要求的愉悦度b[i]


        方法:使用最小费用最大流解决该问题,图共分为四列,第一列源节点,第二列糖果,第三列小孩,第四列汇节点;
        从源点向每个糖果连一条容量为1,价值为0的边;
        对于每个糖果,如果小孩i喜欢该糖果则从该糖果连向该小孩一条容量为1,价值为0的边,否则不连;
        对于每个小孩,计算sum = b[i]/k , rest = b[i] % k,
        若sum!=0,则从小孩向汇点连一条容量为sum,价值-为-k的边;
        若rest!=0,则从小孩向汇点连一条容量为1,价值为-rest的边;
        计算最小费用最大流的流量flow,费用cost,则已产生的愉悦度为-cost,用剩下的糖果能产生的愉悦度为n-flow;
        若-cost + n - flow大于等于b[i]之和,输出yes(注意一定是大于等于,不能是仅仅等于)
        
        顺便说一句,最大费用最大流该怎么求?把每条边的价值取反就行
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
const int inf = 1000000000;
const int maxn = 100;
int n,m,k;
int b[maxn];
int like[maxn][maxn];
typedef long long ll;
struct Edge
{
    int from,to,cap,flow,cost;
};
struct MCMF
{
    int n,m,s,t;
    vector<Edge>edges;
    vector<int>G[maxn];
    int d[maxn],p[maxn],a[maxn],inq[maxn];


    void init(int n)
    {
        this -> n = n;
        for(int i=0;i<=n;i++)       G[i].clear();
        edges.clear();
    }


    void AddEdge(int from,int to,int cap,int cost)
    {
        Edge e1 = {from , to , cap , 0 , cost};
        Edge e2 = {to , from , 0 ,  0 , -cost};
        edges.push_back(e1);
        edges.push_back(e2);
        int m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }


    bool BellmanFord(int s,int t,int &flow , ll& cost)
    {
        for(int i=0;i<=n;i++){
            d[i] = inf;
        }
        d[s] = 0;   p[s] = 0;    a[s] = inf;
        mem(inq,0);     inq[s] = 1;
        queue<int>Q;
        Q.push(s);
        while(!Q.empty()){
            int x = Q.front();      Q.pop();
            inq[x] = 0;
            for(int i= 0;i<G[x].size();i++){
                Edge &e  =  edges[G[x][i]];
                if(e.cap>e.flow && d[e.to]>d[x]+e.cost){
                    d[e.to] = d[x] + e.cost;
                    p[e.to] = G[x][i];
                    a[e.to] = min(a[x],e.cap - e.flow);
                    if(!inq[e.to]){
                        Q.push(e.to);
                        inq[e.to] = 1;
                    }
                }
            }
        }
        if(d[t]==inf)       return false;
        flow += a[t];
        cost += (ll)a[t]*(ll)d[t];
        int u = t;
        while(u!=s){
            edges[p[u]].flow += a[t];
            edges[p[u]^1].flow -= a[t];
            u = edges[p[u]].from;
        }
        return true;
    }


    ll Mincost(int s,int t,int & flow)              //模板的这里用&的目的是为了return cost的同时带回过程中的flow值
    {
        flow = 0;
        ll cost = 0;
        while(BellmanFord(s,t,flow,cost))       ;
        return cost;
    }


};
MCMF g;
int main()
{
    int T,kase = 0;
    scanf("%d",&T);
    while(T--){
        scanf("%d %d %d",&n,&m,&k);
        g.init(n + m + 1);
        int source = 0 , sink = n + m + 1 ;
        int require = 0;        //b[i]之和
        for(int i=1;i<=m;i++){
            scanf("%d",&b[i]);
            require += b[i];
        }
        for(int j = 1;j<=n;j++){
            g.AddEdge(source,j,1,0);
        }
        for(int i = 1;i<=m;i++){
            for(int j = 1;j<=n;j++){
                scanf("%d",&like[i][j]);
                if(like[i][j]){
                    g.AddEdge( j , n + i , 1 , 0);
                }
            }
        }
        for(int i=1;i<=m;i++){
            int sum = b[i]/k;
            int rest = b[i]%k;
            if(sum)
                g.AddEdge( n + i , sink , sum , -k );
            if(rest)
                g.AddEdge(n + i , sink , 1 , -rest);
        }
        int flow;
        ll ans1 = g.Mincost(source , sink,flow);
        int remain = n - flow;
        if(-ans1 + remain >= require){      //一开始wa在这里,不能用  ==  是因为b[i]可以等于零,也就是一块不都不分有些孩子的愉悦度就够了
            printf("Case #%d: YES\n",++kase);
        }
        else{
            printf("Case #%d: NO\n",++kase);
        }
    }
    return 0;
}
0 0
原创粉丝点击