题目描述
Flappy Bird是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。
现在小鸟们遇到了一个难题,他们遇到了一堵巨大的墙,墙上仅有m个洞供他们通过,由于小鸟们的体型不同且墙上洞的形状也不同,所以每种体型的鸟通过每个洞的时间都不同,鸟的体型共有n种,第i种体型的鸟通过第j个洞需要的时间记为T(i,j),且一个洞必须前一只鸟通过之后后一只鸟才能开始通过。
从时刻0开始,鸟开始通过,而每一只鸟的等待时间为从时刻0到自己已经通过洞的时间。现在知道了第i种体型的鸟有pi只,请求出使所有鸟都通过墙的最少的等待时间之和。
数据范围,n<=40,m<=100, ∑ p<=800,T(i,j)<=1000
这种代价复杂麻烦的题,一般只能上网络流
考虑如果一只种类为i的鸟倒数第一个通过洞j,那么对于总代价的贡献为T(i,j),倒数第k则贡献为T(i,j)*k。
根据这个,我们就可以建图了。
源点向每一种鸟连边,对于第i种鸟,连一条流量为p[i]费用为0的边。
对于每一个洞,我们拆成
∑
p个洞,也就是总共拆成了m*
∑
p的洞,每个洞向汇点连一条流量为1费用为0的边。
然后每一种鸟向每一个洞都连一条边。第i种鸟,向第(k-1)*m+j个洞(这里指的是拆后的洞)连一条流量为1,费用为T(i,j)*k的边,这条边若满流则表示的意义为一只种类为i的鸟倒数第k个通过了洞,且该洞为第j个洞(这里指原来的洞)。
然而点数最大为40+100*800,边数最大为40+100*80+40*100*80,直接做显然会超时。
注意到若第(k-1)*m+j个洞向汇点的边没有满流,则第k*m+j个洞显然也不会满流(即还用不到),所以可以动态加边。
代码
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
using namespace std;
const int maxn=3500000+5;
const int ma=100000+5;
int w[maxn],i,j,n,m,num,p[maxn],st,en,sump,t[45][105],c[maxn],k[ma],next[maxn],g[maxn],re[maxn],flow[maxn],now[maxn],last;
int bz[maxn],b1,d[maxn],ans;
void add(int x,int y,int z,int co){
next[++num]=k[x];
k[x]=num;g[num]=y;flow[num]=z,c[num]=co;
re[num]=++num,re[num]=num-1;
next[num]=k[y];k[y]=num;g[num]=x;c[num]=-co;
}
int dfs(int x,int las,int a){
if (x==en){
ans+=d[1];
last=las;
return a;
}
bz[x]=b1;
int i=now[x];
while (i){
int y=g[i];
if ((bz[y]!=b1&&flow[i])&&(d[x]==d[g[i]]+c[i])){
int j=dfs(y,x,min(flow[i],a));
if (j){
now[x]=i;
flow[i]-=j;
flow[re[i]]+=j;
return j;
}
}
i=next[i];
}
now[x]=0;
return 0;
}
bool update(){
int z=100000000,i,j;
fo(j,1,en) if (bz[j]==b1){
int x=j,i=k[x];
while (i){
int y=g[i];
if (bz[y]!=b1&&flow[i]) z=min(z,d[g[i]]+c[i]-d[x]);
i=next[i];
}
}
if (z==100000000) return 0;
fo(j,1,en) if (bz[j]==b1) d[j]+=z;
return 1;
}
int main(){
scanf("%d%d",&n,&m);
fo(i,1,n) scanf("%d",&p[i]),sump+=p[i];
fo(i,1,n) fo(j,1,m) scanf("%d",&t[i][j]);
st=1,en=1+n+m*sump;
fo(i,1,n) add(st,i+1,p[i],0);
if (m==1) {
num=0;fo(i,1,n) fo(j,1,p[i]) w[++num]=t[i][1];
sort(w+1,w+1+sump);
fo(i,1,sump) ans+=w[i]*(sump-i+1);
printf("%d\n",ans);
return 0;
}
fo(i,1,m) {
add(i+1+n,en,1,0);
fo(j,1,n) add(j+1,i+1+n,1,t[j][i]);
}
while (1){
b1++;
fo(i,1,en) now[i]=k[i];
while (dfs(1,1,1)){
b1++;
int w=(last-n-1)%m,w1=(last-n-2)/m+2;if (w==0) w=m;
add(last+m,en,1,0);
fo(i,1,n) add(i+1,last+m,1,t[i][w]*w1);
}
if (!update())break;
}
printf("%d\n",ans);
}