【noi.ac#1769】 A

题目

你一共有 NN 项工作要在 MM 天内完成,每天可以完成的工作数量不限,但是所有工作都一定要完成。

工作之间有一些先后顺序的限制:对于一个限制 A[i],B[i]A[i],B[i] ,需要满足 A[i]A[i] 完成的日期在 B[i]B[i] 之前。

同一项工作在不同的日子完成的收益也不一样,用 x[i][j]x[i][j] 表示第 ii 项工作在第 jj 天完成的收益。特殊地,如果 x[i][j]=−1x[i][j]=−1 则表示我们不能在第 jj 天做第 ii 项工作。

现在,你想要知道你完成所有工作能够获得的收益的最大值。

输入格式
第一行三个整数 N,M,KN,M,K,其中 KK 是限制的个数。

接下来 NN 行,每行包含 MM 个整数,第 ii 行的第 jj 个数表示 x[i][j]x[i][j]。

接下来 KK 行,每行两个整数 A[i],B[i]A[i],B[i],描述一个限制。

输出格式
一个整数,表示能够获得的收益的最大值。

样例一
Input
3 2 2
10 100
100 20
100 30
1 2
1 3
Output
60
样例二
Input
4 5 4
10 -1 100 -1 -1
100 20 -1 -1 -1
100 -1 40 20 30
100 50 60 30 20
1 2
1 3
2 4
3 4
Output
100
数据范围
20%的数据满足:N,M≤10N,M≤10
另有20%的数据满足:N,M,K≤100,B[i]≠B[j]N,M,K≤100,B[i]≠B[j]
对于100%的数据满足:N,M,K≤100,−1≤X[i][j]≤100N,M,K≤100,−1≤X[i][j]≤100,数据保证至少有一组解。

时间限制:1s1s
空间限制:256MB

思路

如果没有限制的话,那么连边方式是将每项工作拆成 M + 1 M+1 M+1 个点 p i , 0 , p i , 1 , ⋯   , p i , m p_{i, 0}, p_{i, 1}, \cdots , p_{i,m} pi,0,pi,1,,pi,m。从 p i , j − 1 p_{i, j-1} pi,j1 p i , j p_{i,j} pi,j 连一条容量为这个工作在第 j j j 天完成要损失的收益的边。最后再连上 S → p i , 0 S \rightarrow p_{i,0} Spi,0 p i , m → T p_{i,m} \rightarrow T pi,mT 这两条边。

对于限制 A i , B i A_i, B_i Ai,Bi,也就是 A i A_i Ai 的割边必须要在 B i B_i Bi 的前面,那么从 p A i , j p_{A_i,j} pAi,j p B i , j + 1 p_{B_i, j+1} pBi,j+1 连上正无穷的边就好了。

这样求出来的最小割就是最少要牺牲的收益。

点数 O ( N M ) O(NM) O(NM),边数 O ( N M + M K ) O(NM+MK) O(NM+MK)

代码

#include<bits/stdc++.h>
using namespace std; 
const int N=1e4+77,M=5*N,K=177; 
int S,T,a[N],b[M],c[M],d[M],e[N],g[K][K],h[N],l,k,m,n,q[N],r,t,x; 
int dfs(int u,int f)
{
	if(u==T)return f; 
	int v=0,w; 
	for(int&i=e[u]; i; i=b[i])if(d[i]&&h[c[i]]==h[u]+1&&(w=dfs(c[i],f<d[i]?f:d[i])))
	{
		f-=w,v+=w,d[i]-=w,d[i^1]+=w; 
		if(!f)return v; 
	}
	return v; 
}
bool bfs()
{
	memset(h+1,-1,T<<2),l=0,r=1; 
	while(l<r)for(int u=q[l++],i=a[u]; i; i=b[i])if(d[i]&&h[c[i]]<0)h[q[r++]=c[i]]=h[u]+1; 
	return h[T]>=0; 
}
void add(int u,int v,int w){b[++t]=a[u],c[a[u]=t]=v,d[t]=w,b[++t]=a[v],c[a[v]=t]=u; }
int main()
{
	scanf("%d%d%d",&n,&m,&k); 
	for(int i=1; i<=n; i++)for(int j=1; j<m; j++)g[i][j]=++t; 
	T=++t; 
	for(int i=t=1; i<=n; i++)g[i][m]=T; 
	for(int i=1; i<=n; i++)
	for(int j=0,w; j<m; j++)scanf("%d",&w),add(g[i][j],g[i][j+1],w==-1?0x3f3f3f3f:100-w); 
	for(int i=1,u,v; i<=k; i++)
	{
		scanf("%d%d",&u,&v); 
		for(int j=0; j<m; j++)add(g[u][j],g[v][j+1],0x3f3f3f3f); 
	}
	while(bfs())memcpy(e,a,T+1<<2),x+=dfs(S,0x3f3f3f3f); 
	printf("%d",n*100-x); 
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值