/*
题目描述:有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;
}
题目描述:有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;
}