单单用最大流显然是不能达到最佳分配的,应该使用费用流。
费用很显然,对于喜欢的糖,费用就是K,我一开始想的是用
b[i]/k
来表示容量上限,但后来发现这样是不对的,因为可能最后一颗糖加上之后费用超过了
b[i]
,但实际得到的快乐值只有
b[i]
,所以要对
b[i]%k
进行讨论
1. 若
b[i]%k==0
那么把连接人到超级汇点的边容量为
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])−maxcost≤n−maxflow 就能用剩下的糖果完成分配。
#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;
}