题目
你一共有 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,j−1 到 p i , j p_{i,j} pi,j 连一条容量为这个工作在第 j j j 天完成要损失的收益的边。最后再连上 S → p i , 0 S \rightarrow p_{i,0} S→pi,0 和 p i , m → T p_{i,m} \rightarrow T pi,m→T 这两条边。
对于限制 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;
}