飛揚的小鳥

題目描述

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);
}
發佈了80 篇原創文章 · 獲贊 21 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章