題目描述
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的邊。
對於每一個洞,我們拆成
然後每一種鳥向每一個洞都連一條邊。第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);
}