【IOI2005】河流

++ i

目錄

題目

描述

輸入

輸出

樣例輸入

樣例輸出

分析

狀態轉移

彙總代碼


題目

描述

幾乎整個Byteland王國都被森林和河流所覆蓋。小點的河匯聚到一起,形成了稍大點的河。就這樣,所有的河水都匯聚並流進了一條大河,最後這條大河流進了大海。這條大河的入海口處有一個村莊——名叫Bytetown

在Byteland國,有n個伐木的村莊,這些村莊都座落在河邊。目前在Bytetown,有一個巨大的伐木場,它處理着全國砍下的所有木料。木料被砍下後,順着河流而被運到Bytetown的伐木場。Byteland的國王決定,爲了減少運輸木料的費用,再額外地建造k個伐木場。這k個伐木場將被建在其他村莊裏。這些伐木場建造後,木料就不用都被送到Bytetown了,它們可以在 運輸過程中第一個碰到的新伐木場被處理。顯然,如果伐木場座落的那個村子就不用再付運送木料的費用了。它們可以直接被本村的伐木場處理。

注意:所有的河流都不會分叉,也就是說,每一個村子,順流而下都只有一條路——到bytetown。

國王的大臣計算出了每個村子每年要產多少木料,你的任務是決定在哪些村子建設伐木場能獲得最小的運費。其中運費的計算方法爲:每一塊木料每千米1分錢。

編一個程序:

1.從文件讀入村子的個數,另外要建設的伐木場的數目,每年每個村子產的木料的塊數以及河流的描述。

2.計算最小的運費並輸出。

輸入

第1行:包括兩個數 n(2<=n<=100),k(1<=k<=50,且 k<=n)。n爲村莊數,k爲要建的伐木場的數目。除了bytetown外,每個村子依次被命名爲1,2,3……n,bytetown被命名爲0。

接下來n行,每行3個整數 wi——每年i村子產的木料的塊數 (0<=wi<=10000) vi——離i村子下游最近的村子(即i村子的父結點)(0<=vi<=n) di——vi到i的距離(千米)。(1<=di<=10000)

保證每年所有的木料流到bytetown的運費不超過2000,000,000分 50%的數據中n不超過20。

輸出

輸出最小花費,精確到分。

樣例輸入

4 2
1 0 1
1 1 10
10 2 5
1 2 3

樣例輸出

4

分析

此題如果只定義 dp[i][j] 顯然是做不出來的,可以想到其中 i 到伐木場的距離是不可求的(我們並不知道離他最近的伐木場在哪,甚至除了根節點外的其他任何一個伐木場都找不到)所以我們需要再加一維來表示伐木場的位置。然後我們可以再用一維表示 i 節點上是否建立了伐木場。

狀態轉移

  1. dp[i][j][k][0] = min(dp[i][j][k][0],dp[son][fa][re][0] + dp[i][fa][k - re][0]);
  2. dp[i][j][k][1] = min(dp[i][j][k][1],dp[son][x][re][0] + dp[x][x][k - re][1]);

至於 j 該如何求呢? j 就是 i 本身或者是它的祖先,所以我們只需要加一個 ancestor數組,在每一次進行 dfs時,在開頭記錄一下這個節點,然後在最後把它刪了就可以了。

既然知道了狀態轉移方程,就要知道初始化,否則就會無限輸出 0,對吧……

顯然上面的 dp 裝的是 以 i 爲根的子樹中,除 i 以外所以節點到 j 的距離,所以

  • dp[i][j][k][0] += dp[i][j][0][0];
  • dp[i][j][k][1] += dp[i][i][0][0];

將已經統計過的節點算進去。

for (reg int j = 1;j <= tot; ++ j){
     int fa = ancestor[j];
         for (reg int k = m;k >= 0; -- k){
              F[x][fa][k][0] += F[s][fa][0][0];
              F[x][fa][k][1] += F[s][x][0][0];
              for (reg int re = 0;re <= k; ++ re){
                  F[x][fa][k][0] = min(F[x][fa][k][0],F[s][fa][re][0] + F[x][fa][k - re][0]);
                  F[x][fa][k][1] = min(F[x][fa][k][1],F[s][x][re][0] + F[x][x][k - re][1]);
                }
            }
        }
    }

然而,就這樣的話,還是會輸出 0 ,爲什麼?

我們沒有加上 x 到他父親的運費,同時的話,我們也可以比較一下f[x][fa][k][0] 和 f[x[fa][k][1]的大小關係,這樣就能保證答案的最優性

 for (reg int i = 1;i <= tot; ++ i){
        int fa = ancestor[i];
        for (reg int j = m;j >= 0; -- j){
            if (j == 0)
                F[x][fa][j][0] += wood[x] * (dis[x] - dis[fa]);
            else F[x][fa][j][0] = min(F[x][fa][j][0] + 1ll * wood[x] * (dis[x] - dis[fa]),F[x][fa][j - 1][1]);
        }
    }

彙總代碼

#include<cstdio>
#include<vector>
#define LL long long
#define M 200
#define Maxn 50
#define reg register
#define min(A,B) (A < B ? A : B)
using namespace std;
 
vector < int > G[M + 5];
int n,m,tot;
int F[M + 5][M + 5][Maxn + 5][2];
int ancestor[M + 5],dis[M + 5],wood[M + 5];

void dfs(int x,int fa){
    ancestor[ ++ tot] = x;
    for (reg int i = 0;i < G[x].size(); ++ i){
        int s = G[x][i];
        if (s == fa)
            continue;
        dis[s] += dis[x];
        dfs(s,x);
        for (reg int j = 1;j <= tot; ++ j){
            int fa = ancestor[j];
            for (reg int k = m;k >= 0; -- k){
                F[x][fa][k][0] += F[s][fa][0][0];
                F[x][fa][k][1] += F[s][x][0][0];
                for (reg int re = 0;re <= k; ++ re){
                    F[x][fa][k][0] = min(F[x][fa][k][0],F[s][fa][re][0] + F[x][fa][k - re][0]);
                    F[x][fa][k][1] = min(F[x][fa][k][1],F[s][x][re][0] + F[x][fa][k - re][1]);
                }
            }
        }
    }
    for (reg int i = 1;i <= tot; ++ i){
        int fa = ancestor[i];
        for (reg int j = m;j >= 0; -- j){
            if (j == 0)
                F[x][fa][j][0] += wood[x] * (dis[x] - dis[fa]);
            else F[x][fa][j][0] = min(F[x][fa][j][0] + 1ll * wood[x] * (dis[x] - dis[fa]),F[x][fa][j - 1][1]);
        }
    }
    ancestor[tot -- ] = 0;
}
 
int main(){
    scanf("%d%d",&n,&m);
    for (reg int i = 1;i <= n; ++ i){
        int v;
        scanf("%d%d%d",&wood[i],&v,&dis[i]);
        G[i].push_back(v);
        G[v].push_back(i);
    }
    dfs(0,-1);
    printf("%d\n",F[0][0][m][0]);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章