CDOJ 1351(樹形DP)

題意&思路:

題意:帶權邊樹上,在起點0出發,初始值爲V ,求最多能訪問的節點數目?

如果數據小一點,我們可以考慮 dp[u][i] ,表示u節點及其子樹花費i最多能夠訪問的節點數,這個是很好做的,樹上揹包嘛。

但是題目多詢問,於是考慮 dp[u][i][o] ,表示當前節點u及其子樹,訪問了 i 個節點,並且是否停留在u點的最小花費。

這樣的話,會有以下幾種情況:
U 爲父節點,v 爲子節點
1. 訪問了v 子樹j個點 並且 在v點
訪問了u已處理子樹i個點 並且不在u點
最後從v回到 u已處理子樹中的某一個點
update(dp[u][i+j][0],dp[u][i][0]+dp[v][j][1]+2d);
2. 訪問了v 子樹j個點 並且不在v點
訪問了u已處理子樹i個點 並且在u點
最後回到了v子樹中的某一個點
update(dp[u][i+j][0],dp[u][i][1]+dp[v][j][0]+d);
3. 訪問了v 子樹j個點 並且在v點
訪問了u已處理子樹i個點 並且在u點
但是回到了v點(也就是停在了v點)
update(dp[u][i+j][0],dp[u][i][1]+dp[v][j][1]+d);
4. 問了v 子樹j個點 並且在v點
訪問了u已處理子樹i個點 並且在u點
最後回到了u點
update(dp[u][i+j][1],dp[u][i][1]+dp[v][j][1]+2d);

代碼:

#include <bits/stdc++.h>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int>  P;
const int N = 505,MOD = 7+1e9;
vector<P> G[N];
int dp[N][N][2];
void init(int n)
{
    for(int i = 0;i < n;i ++)
    {
        dp[i][0][0] = dp[i][0][1] = 0;
        for(int j = 1;j <= n;j ++) dp[i][j][0] = dp[i][j][1] = INF;
    }
}
void update(int& x, int y)
{
    x = min(x, y);
}
int dfs(int u, int fa)
{
    dp[u][1][1] = 0;
    int sum = 1;
    for(auto it : G[u])
    {
        int v = it.FT, d = it.SD;
        if(v == fa) continue;
        int sz = dfs(v, u);
        for(int i = sum;i > 0;i --)
        {
            for(int j = sz;j > 0;j --)
            {
                update(dp[u][i+j][0], dp[u][i][0] + dp[v][j][1] + 2 * d);
                update(dp[u][i+j][0], dp[u][i][1] + dp[v][j][0] +     d);
                update(dp[u][i+j][0], dp[u][i][1] + dp[v][j][1] +     d);
                update(dp[u][i+j][1], dp[u][i][1] + dp[v][j][1] + 2 * d);                
            }
        }
        sum += sz;
    }
    return sum;
}
int main()
{
    int n;
    scanf("%d", &n);
    init(n);
    for(int i = 1;i < n;i ++)
    {
        int u, v, d;
        scanf("%d%d%d", &u, &v, &d);
        G[u].PB({v,d});
        G[v].PB({u,d});
    }
    dfs(0, -1);
    int Q;
    scanf("%d", &Q);
    while(Q --)
    {
        int x, ans = 0;
        scanf("%d", &x);
        for(int i = n;i >= 1;i --)
        {
            if(dp[0][i][0] <= x)
            {
                ans = i;
                break;
            }
        }
        printf("%d\n", max(1, ans));
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章