UVALive 4015 Caves

        树形dp,自己想了很久很久都没想起来怎么做。。。汗,真笨啊。只好看了一下别人的思路,挺巧妙的,原来树形dp的第三维这么能这么表示,看来以后要让自己的思维更加发散些才能做的出来这种题了。dp[u][l][f] ,u表示当前节点,l表示该节点能到达l个节点所走路程的最小值。当f = 1时表示不用回到当前点,f = 0表示必须回到当前点。则状态转移方程可以表示为:

       dp[u][j][0] = min(dp[u][j][0], dp[v][k][0] + dp[u][j - k][0] + 2 * E[i].d);
       dp[u][j][1] = min(dp[u][j][1], min(dp[v][k][0] + dp[u][j - k][1] + 2 * E[i].d, dp[v][k][1] + dp[u][j - k][0] + E[i].d));

知道了状态转移方程就好写多了~~~

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>

#define LL long long
#define CLR(a, b) memset(a, b, sizeof(a))

using namespace std;

const int N = 555;
const int INF = 1e9;

struct Edge
{
    int u, v, d;
}E[N * 2];

int fir[N], next[N * 2], tot;
int dp[N][N][2];
int ans[N], n;
int sum[N];

void Add_edge(int u, int v, int d)
{
    E[tot].u = u, E[tot].v = v, E[tot].d = d;
    next[tot] = fir[u], fir[u] = tot ++;
}

int B_ser(int l, int r, int key)
{
    int m;
    while(l <= r)
    {
        m = (l + r) >> 1;
        if(ans[m] == key) return m;
        else if(ans[m] < key) l = m + 1;
        else r = m - 1;
    }
    return r;
}

void dfs(int u, int f)
{
    int v, i, j, k;
    for(i = fir[u]; ~i; i = next[i])
    {
        v = E[i].v;
        if(v != f)
        {
            dfs(v, u);
            sum[u] += sum[v];
            for(j = sum[u]; j > 1; j --)
            {
                for(k = min(j - 1, sum[v]); k > 0; k --)
                {
                    dp[u][j][0] = min(dp[u][j][0], dp[v][k][0] + dp[u][j - k][0] + 2 * E[i].d);
                    dp[u][j][1] = min(dp[u][j][1], min(dp[v][k][0] + dp[u][j - k][1] + 2 * E[i].d, dp[v][k][1] + dp[u][j - k][0] + E[i].d));
                }
            }
        }
    }
}

int main()
{
    //freopen("input.txt", "r", stdin);
    int u, v, i, d, q, l, j, cas = 1;
    while(scanf("%d", &n), n)
    {
        printf("Case %d:\n", cas ++);
        tot = 0;
        CLR(fir, -1);CLR(dp, 0);
        sum[0] = 1;
        for(i = 1; i < n; i ++)
        {
            sum[i] = 1;
            scanf("%d%d%d", &u, &v, &d);
            Add_edge(u, v, d);
            Add_edge(v, u, d);
        }
        for(i = 0; i < n; i ++)
        {
            for(j = 2; j <= n; j ++)
            {
                dp[i][j][0] = dp[i][j][1] = INF;
            }
            dp[i][1][0] = dp[i][1][1] = 0;
        }
        dfs(0, -1);
        for(i = 1; i <= n; i ++)
        {
            ans[i] = min(dp[0][i][0], dp[0][i][1]);
        }
        scanf("%d", &q);
        while(q --)
        {
            scanf("%d", &l);
            printf("%d\n", B_ser(1, n, l));
        }
    }
}


                

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值