• joj 2453 candy 网络流建图的题


    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

    2
    4 3
    1 2 1
    2 1 1
    1 1 2
    1 2 2
    3 2 2
    1 1
    1
    2
    

    Sample Output

    Yes
    No
    

    网络流,主要是建图
    分配的时候肯定会优先给每个孩子分配喜欢的糖果,所以先只考虑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 1e8
    using 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];
    	}
    
    	//dfs
    	int 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
    ";
    	else
    		cout<<"No
    ";
    }
    
    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");
    		else
    			puts("No");
    	}
    	return 0;
    }
    




  • 相关阅读:
    DataTable、GridView、DataList导出至Word或Excel
    实现字符串转换为图片
    Sina App Engine与Google App Engine区别对比
    C/C++反序输出字符串总结
    NAND flash和NOR flash详解
    [转载]看完《python简明教程》笔记及第一个python程序
    Python写爬虫——抓取网页并解析HTML
    ACM HDU 1004 Let the Balloon Rise
    有向图的强连通分量
    ACM HDU 1005 Number Sequence
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3263029.html
Copyright © 2020-2023  润新知