Hdu 6126 Give out candies 最小割

Give out candies

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 517    Accepted Submission(s): 170


Problem Description
There are  n  children numbered  1  to  n , and HazelFan's task is to give out candies to the children. Each children must be given at least  1  and at most  m  candies. The children raised  k  pieces of requirements, and every piece includes three integers  x,y,z , means that the number of candies given to the child numbered  x , subtract the number of candies given to the child number  y , the result should not be more than  z . Besides, if the child numbered  i  is given  j  candies, he or she will get  wi,j satisfaction score, now you need to help HazelFan maximize the sum of the satisfaction scores of these children.
 

Input
The first line contains a positive integer  T(1T5) , denoting the number of test cases.
For each test case:
The first line contain three positive integers  n,m,k(1n,m50,1k150) .
The next  n  lines, the  i th line contains  m  positive integers, the  j th integer denotes  wi,j .
The next  k  lines, each line contains three integers  x,y,z(1x,yn,z<233) , denoting a piece of requirement.
 

Output
For each test case:
A single line contains a nonnegative integer, denoting the answer. Particularly, if there is no way to give out candies, print -1.
 

Sample Input
  
  
2 2 1 1 1 1 1 2 1 3 3 2 1 2 3 2 3 1 3 1 2 1 2 0 2 3 0
 

Sample Output
  
  
2 7
 

Source


很巧妙的一个建图

此时应上ACM真有意思的表情包


n个人分糖,第i个人得到j个糖的收益是Wij,可以分得的糖果数是[1,m]。有k个限制,要求第X个人的糖数量和第Y个人的糖数量满足:

xi-yi<=zi

问最大收益。


这题求最大值,怎么会有最小割呢?

其实是把边的流量取反,但跑最大流算法的时候流量不可能是负的,于是我们给流量加上一个固定值使得流量为正。

如果不考虑限制条件,设Ai,j表示第i个人拿j个糖的点,则应该把Ai,j和Ai,j+1连上流量MAX-Wij, Ai,m和汇点连上流量为MAX-Ai,m, 源点和Ai,1连上流量inf(inf>>MAX)的边,此时最小割s即最优分法,答案是n*MAX-s。

对于某个限制条件Xi-Yi<=Zi,可以把所有Ax,j和Bx,j-z之间连上流量inf的边,这样,当不满足限制的割边发生时,必定要割某条流量为inf的边。

若最后跑出的最小割比inf大则说明不能满足所有限制条件,则无解,否则答案就是刚刚所提到的n*MAX-s.


#include <cstdio>
#include <iostream>
#include <string.h>
#include <string> 
#include <map>
#include <queue>
#include <deque>
#include <vector>
#include <set>
#include <algorithm>
#include <math.h>
#include <cmath>
#include <stack>
#include <iomanip>
#define mem0(a) memset(a,0,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
const int maxn=55,maxk=100005,inf=0x3f3f3f3f;  
const ll llinf=0x3f3f3f3f3f3f3f3f;   
const ld pi=acos(-1.0L);
int a[maxn][maxn];
int head[maxn*maxn],dist[maxn*maxn],current[maxn*maxn];
int num;

struct Edge {  
    int from,to,flow,pre;  
};  
Edge edge[maxk];  
  
void addedge(int from,int to,int flow) {  
    edge[num]=(Edge){from,to,flow,head[from]};  
    head[from]=num++;  
    edge[num]=(Edge){to,from,0,head[to]};  
    head[to]=num++;  
}  
  
bool bfs (int n) {  
    queue<int> q;  
    q.push(0);  
    memset(dist,-1,sizeof(dist));  
    dist[0]=0;  
    while (!q.empty()) {  
        int now=q.front();  
        q.pop();  
        for (int i=head[now];i!=-1;i=edge[i].pre) {  
            int to=edge[i].to;  
            if (dist[to]==-1&&edge[i].flow>0) {  
                dist[to]=dist[now]+1;  
                q.push(to);  
            }  
        }  
    }  
    return dist[n]!=-1;  
}  
  
int dfs(int now,int flow,int n) {  
    int f;  
    if (now==n) return flow;  
    for (int i=current[now];i!=-1;i=edge[i].pre) {  
        int to=edge[i].to;  
        current[now]=i;  
        if (dist[now]+1==dist[to]&&edge[i].flow>0&&  
        (f=dfs(to,min(flow,edge[i].flow),n))) {  
            edge[i].flow-=f;  
            edge[i^1].flow+=f;  
            return f;  
        }  
    }  
    return 0;  
}  
  
int dinic(int n) {  
    int sum=0,f;  
    while (bfs(n)) {  
        memcpy(current,head,sizeof(head));  
        while (f=dfs(0,inf,n)) sum+=f;  
    }  
    return sum;  
}  

int main() {
	int cas;
	scanf("%d",&cas);
	while (cas--) {
		int n,m,k,i,j,x,y,z;
		memset(head,-1,sizeof(head));
		num=0;
		scanf("%d%d%d",&n,&m,&k);
		for (i=1;i<=n;i++) {
			scanf("%d",&a[i][1]);
			addedge(0,(i-1)*m+1,1e7);
			for (j=2;j<=m;j++) {
				scanf("%d",&a[i][j]);
				addedge((i-1)*m+j-1,(i-1)*m+j,1000-a[i][j-1]);
			}
			addedge(i*m,n*m+1,1000-a[i][m]);
		}
		for (i=1;i<=k;i++) {
			scanf("%d%d%d",&x,&y,&z);
			for (j=max(z+1,1);j<=m&&j-z<=m;j++) {
				addedge((x-1)*m+j,(y-1)*m+j-z,1e7);
			}
			if (j-z>m&&j<=m)
				addedge((x-1)*m+j,n*m+1,1e7);
		}
		int ans=dinic(n*m+1);
		if (ans>1e7) printf("-1\n"); else printf("%d\n",n*1000-ans);
	} 
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值