joj 2453 candy 网络流建图的题

来源:互联网 发布:qt 网络编程 编辑:程序博客网 时间:2024/06/17 05:01

Problem D: Candy

As a teacher of a kindergarten, you have many things to do during a day, one of which is to allot candies to all children in your class. Today you have N candies for the coming M children. Each child likes different candy, and as a teacher who know them well, you can describe how the child i likes the candy j with a number Aji (Aji = 2 if the child i likes the candy j, or else Aji = 1).

The child i feels happy while ( Cij = 1 if the child i get the candy j, or else Cij = 0). Now your task is to allot the candies in such a way that makes every child happy (of course except you, ^_^).

Input

The first line of the input contains a single integer T (1 <= T <= 10), representing the number of cases that follow.

The first line of each case consists of two integers N and M (1 <= N <= 100000, 1 <= M <= 10), which are the number of candies and the number of children.

There are N lines following, the ith line containing M integers: Ai1, Ai2, Ai3, ..., AiM (1 <= Aij <= 2)

The last line of the case consists of M integers: B1, B2, B3, ..., BM (0 <= Bi <= 1000000000).

Output

For each case, if there is a way to make all children happy, display the word “Yes”. Otherwise, display the word “No”.

Sample Input

24 31 2 12 1 11 1 21 2 23 2 21 112

Sample Output

YesNo

网络流,主要是建图
分配的时候肯定会优先给每个孩子分配喜欢的糖果,所以先只考虑Aij=2的孩子和糖果(i,j)。
如果Ai,j=2,那么把孩子i向糖果j连一条容量为1的边,再建立源点S,向每个孩子连一条容量为Bi/2的边(因为每个开心值为2的糖果只算1,所以孩子的B值也要先除以2),最后把每个糖果向汇点T连容量为1的边,做一次网络最大流。
假设S到孩子i的流量为fi,说明孩子i已经获得了fi*2点快乐值,还需要Bi-fi*2点,这时候f1+f2+..+fm是总共分出去的糖果数,那么还剩N-(f1+f2+..+fm)个糖果,如果这个数>=sigma(Bi-fi*2),即剩余的糖果数大于等于孩子还需要的总共快乐值,则有解,否则无解
PS:每个孩子平均能吃10000个糖,我真是无限ORZ
以下使用的是刘汝佳白书上的DINIC算法模板做的

#include<iostream>#include<algorithm>#include<cstring>#define size_num 100200#include<vector>#include<queue>#define INF 1e8using namespace std;int child[105];struct Dinic{struct Edge{int from,to,cap,flow;};vector<Edge> edges;//边表。edges[e]和edges[e+1]互为反向弧,//注意到e必须是偶数即是大的奇数与比他小的偶数互为反向边,即e与e^1互为反向边vector<int> G[size_num];//领接表,G[i][j]表示节点i的第j条边在e数组中的序号void add_edge(int from,int to,int cap){edges.push_back((Edge){from,to,cap,0});//加入正向边edges.push_back((Edge){to,from,0,0});//加入反向边int m=edges.size();G[from].push_back(m-2);//存的是边的位子G[to].push_back(m-1);//貌似有一种静态链表的感觉}int s,t;//源点编号和汇点编号bool vis[size_num];//bfs时使用int d[size_num];//从起点到i的距离int cur[size_num];//当前弧的下标void init(){edges.clear();for(int i=0;i<size_num;i++)G[i].clear();}bool bfs(){memset(vis,0,sizeof(vis));queue<int > q;q.push(s);d[s]=0;vis[s]=1;while(!q.empty()){int x=q.front();q.pop();for(int i=0;i<G[x].size();i++){Edge&e=edges[G[x][i]];if(!vis[e.to]&&e.cap>e.flow){vis[e.to]=1;d[e.to]=d[x]+1;q.push(e.to);}}}return vis[t];}//dfsint dfs(int x,int a){if (x==t||a==0) return a;int flow=0,f;for(int &i=cur[x];i<G[x].size();i++)//从上次考虑的弧{Edge &e=edges[G[x][i]];if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){e.flow+=f;//增加正向的流量edges[G[x][i]^1].flow-=f;//减少反向的流量flow+=f;a-=f;if(a==0) break;}}return flow;}//int maxflow(int s,int t){this->s=s;this->t=t;int flow=0;while(bfs()){memset(cur,0,sizeof(cur));flow+=dfs(s,INF);}return flow;}}solve;void read(){solve.init();int n,m;//糖果数量和孩子的数量cin>>n>>m;int s=0,t=1+m+n;//solve->n=t+1;//1->m表示孩子,m+1->m+n表示糖果for(int i=1;i<=n;i++){solve.add_edge(i+m,t,1);for(int j=1;j<=m;j++){int temp;cin>>temp;if(temp==2)solve.add_edge(j,m+i,1);}}long long sum=0;for(int i=1;i<=m;i++){cin>>child[i];sum+=child[i];solve.add_edge(s,i,child[i]/2);}int f=solve.maxflow(s,t);int yu=n-f;if(sum<=yu+f*2)cout<<"Yes\n";elsecout<<"No\n";}int main(){int T;cin>>T;while(T--)read();return 0;}

以下是不用vector的代码比较快0.5s上一个是3.07秒

#include <queue>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>const int maxn = 100055;const int maxm = 600005;const int inf = 0x3f3f3f3f;struct MaxFlow{int net[maxn], gap[maxn], dis[maxn], pre[maxn], cur[maxn];int siz, n;std::queue <int> Q;struct EDGE{int v, cap, next;EDGE(){}EDGE(int a, int b, int c): v(a), cap(b), next(c){}}E[maxm<<1];void init(int _n)//要传入节点数{n = _n, siz = 0;memset(net, -1, sizeof(net));}void add_edge(int u, int v, int cap)//加边操作{E[siz] = EDGE(v, cap, net[u]);net[u] = siz++;E[siz] = EDGE(u, 0, net[v]);net[v] = siz++;}void bfs(int st)//广搜{int u, v;for(int i = 0; i <= n; i++)dis[i] = n, gap[i] = 0;gap[0] = 1, dis[st] = 0;Q.push(st);while(!Q.empty()){u = Q.front();Q.pop();for(int i = net[u]; i != -1; i = E[i].next){v = E[i].v;if(!E[i^1].cap || dis[v] < n)continue;dis[v] = dis[u] + 1;gap[dis[v]]++;Q.push(v);}}}int isap(int st, int en)//st 是源点 en 是汇点{int u = pre[st] = st, ma = 0, aug = inf, v;bfs(en);for(int i = 0; i <= n; i++)cur[i] = net[i];while(dis[st] <= n){loop:for(int &i = cur[u]; v = E[i].v, i != -1; i = E[i].next)if(E[i].cap && dis[u] == dis[v] + 1){aug = std::min(aug, E[i].cap);pre[v] = u, u = v;if(v == en){ma += aug;for(u = pre[u]; v != st; v = u, u = pre[u]){E[cur[u]].cap -= aug;E[cur[u]^1].cap += aug;}aug = inf;}goto loop;}int mi = n;for(int i = net[u]; v = E[i].v, i != -1; i = E[i].next)if(E[i].cap && mi > dis[v]){cur[u] = i;mi = dis[v];}if(--gap[dis[u]] == 0)break;gap[dis[u]=mi+1]++;u = pre[u];}return ma;}};MaxFlow G;int main(){int t, n, m, st, en, temp;long long sum;scanf("%d", &t);while(t--){scanf("%d %d", &n, &m);st = 0, en = n + m + 1;G.init(en);for(int i = 1; i <= n; i++)for(int j = 1; j <= m; j++){scanf("%d", &temp);if(temp == 2)G.add_edge(i, j+n, 1);}sum = 0;for(int i = 1; i <= m; i++){scanf("%d", &temp);sum += temp;}for(int i = 1; i <= n; i++)G.add_edge(st, i, 1);for(int i = 1; i <= m; i++)G.add_edge(i+n, en, inf);if(((long long)n+G.isap(st, en)) >= sum)puts("Yes");elseputs("No");}return 0;}




原创粉丝点击