HDU 4322 Candy 最大费用流+巧妙建图

单单用最大流显然是不能达到最佳分配的,应该使用费用流。
费用很显然,对于喜欢的糖,费用就是K,我一开始想的是用 b[i]/k 来表示容量上限,但后来发现这样是不对的,因为可能最后一颗糖加上之后费用超过了 b[i] ,但实际得到的快乐值只有 b[i] ,所以要对 b[i]%k 进行讨论
1. 若 b[i]%k==0 那么把连接人到超级汇点的边容量为 b[i]/k ,费用为 k ,即 (b[i]/k,k)
2. 若 b[i]%k!=0 那么分别连两条边即 (b[i]/k,k)(1,b[i]%k)

其它都是很正常的连边,即从超级源点到各个糖果,容量为1,费用为0。还有从糖果到喜欢这个糖果的人,容量为1,费用为0。

这样跑完最大费用最大流之后,得到maxcost和maxflow,分别表示当前的快乐值之和和使用糖果数,那么剩下的糖果都只能产生1的快乐值,所以如果 tot(b[i])maxcostnmaxflow 就能用剩下的糖果完成分配。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>

using namespace std;
const int MAXN = 1000;
const int MAXM = 1e5+100;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int to,cap,flow,cost,nex;
    Edge(int to,int cap,int flow,int cost,int nex):to(to),cap(cap),flow(flow),cost(cost),nex(nex) {}
    Edge() {}
}edge[MAXM];
int head[MAXN],dis[MAXN],tol,pre[MAXN],b[MAXN],tot,n,m,k;
bool vis[MAXN];

void init()
{
    memset(head,-1,sizeof head);
    tol=tot=0;
}

void addedge(int u,int v,int cap,int cost)
{
    edge[tol] = Edge(v,cap,0,cost,head[u]);
    head[u] = tol++;
    edge[tol]  =Edge(u,0,0,-cost,head[v]);
    head[v] = tol++;
}

bool spfa(int s,int t)
{
    memset(vis,0,sizeof vis);
    memset(dis,0x3f,sizeof dis);
    memset(pre,-1,sizeof pre);
    queue<int> que;
    que.push(s);
    dis[s]=0;
    while (!que.empty())
    {
        int u=que.front();que.pop();
        vis[u]=false;
        for (int i=head[u];~i;i=edge[i].nex)
        {
            int v=edge[i].to;
            if (edge[i].cap > edge[i].flow &&
                dis[v] > dis[u] + edge[i].cost)
            {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if (!vis[v])
                {
                    que.push(v);
                    vis[v]=true;
                }
            }
        }
    }
    if (~pre[t])
        return true;
    else
        return false;
}

int MCMF(int s,int t,int &cost)
{
    int flow=0,f,tmp=0;
    cost=0;
    while (spfa(s,t))
    {
        int tmp = INF;
        for (int i=pre[t];~i;i=pre[edge[i^1].to]) tmp=min(edge[i].cap-edge[i].flow,tmp);
        for (int i=pre[t];~i;i=pre[edge[i^1].to])
        {
            edge[i].flow += tmp;
            edge[i^1].flow -= tmp;
            cost += tmp*edge[i].cost;
        }
        flow += tmp;
    }
    return flow;
}


int main()
{
    int t;
    scanf("%d",&t);
    for (int tt=1;tt<=t;tt++)
    {
        init();
        int tar=600;
        scanf("%d%d%d",&n,&m,&k);//1--n candy n+1--n+m man n+m+1--n+2m man'
        for (int i=1;i<=n;i++) addedge(0,i,1,0);
        for (int i=1;i<=m;i++)
        {
            scanf("%d",&b[i]);
            tot += b[i];
            if (b[i] % k == 0)
            {
                addedge(n+i,tar,b[i]/k,-k);
            }
            else
            {
                addedge(n+i,tar,b[i]/k,-k);
                addedge(n+i,tar,1,-(b[i]%k));
            }
        }
        for (int i=1;i<=m;i++)
            for (int j=1;j<=n;j++)
            {
                int tmp;
                scanf("%d",&tmp);
                if (tmp==1) addedge(j,n+i,1,0);
            }
        int maxcost;
        int maxflow=MCMF(0,tar,maxcost);
        maxcost=-maxcost;
        if (tot - maxcost <= n-maxflow)
            printf("Case #%d: YES\n",tt);
        else
            printf("Case #%d: NO\n",tt);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/z631681297/article/details/78347981