HDU 4276

 2012年长春赛区网络赛的第十题,比赛时很幸运1Y了。。   

        这题无解的情况很容易判断。如果有解,可以如下理解:从1 走到 n是一条必经路径,在这条路径上的每个节点,都可以在非必经节点中绕一圈再回到那个节点,可以想象成在一条路径上每个节点都允许扩展一个圈。因此,首先找出这条路径,对所有必经节点做一次树形DP,dp[u][i]表示从节点u出发,经过i时间回到节点u能获得的最大价值。最后就是决策每个必经节点应该怎样扩展圈使全局价值最大,这里再DP一下就好了。

/****************************************************************
    Problem: HDU 4276
    User: Jeflie
    Language: C++
    Result: Accepted
    Time: 46 ms
    Memory: 776 kb
****************************************************************/
 
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int INF = 1e9;
 
struct Data
{
    int y, w;
    Data (int a, int b): y(a), w(b) {}
};
vector<Data> G1[110];
int dp[110][510];
int A[110], mark[110];
 
void DFS(int u, int pre, int Max)
{
    dp[u][0] = A[u];
    for (int i = 0; i < G1[u].size(); i++)
    {
        int y = G1[u][i].y;
        int w = G1[u][i].w;
        if (y == pre || mark[y]) continue;
        DFS(y, u, Max);
        for (int j = Max; j >= 0; j--) if (dp[u][j] != -1)
            for (int k = Max; k >= 0; k--)
                if (dp[y][k] != -1 && j + k + 2*w <= Max)
                    dp[u][j+k+2*w] = max(dp[u][j+k+2*w], dp[u][j] + dp[y][k]);
    }
}
 
int BFS(int s, int e)  // 比赛时顺手写了个最短路,其实怎么搞都行
{
    int dist[110], path[110];
    for (int i = 0; i < 110; i++)
        dist[i] = INF;
    path[s] = -1;
    dist[s] = 0;
    queue<int> que;
    que.push(s);
    while (!que.empty())
    {
        int u = que.front();
        que.pop();
        for (int i = 0; i < G1[u].size(); i++)
        {
            Data &v = G1[u][i];
            if (dist[u] + v.w < dist[v.y])
            {
                path[v.y] = u;
                dist[v.y] = dist[u] + v.w;
                que.push(v.y);
            }
        }
    }
    for (int i = e; i != -1; i = path[i])
        mark[i] = 1;
    return dist[e];
}
 
int main()
{
    int n, t;
    while (cin>>n>>t)
    {
        memset(G1, 0, sizeof(G1));
        memset(dp, -1, sizeof(dp));
        memset(mark, 0, sizeof(mark));
        int a, b, c;
        for (int i = 1; i < n; i++)
        {
            cin>>a>>b>>c;
            G1[a].push_back(Data(b, c));
            G1[b].push_back(Data(a, c));
        }
        for (int i = 1; i <= n; i++)
            cin>>A[i];
 
        int tt = BFS(1, n);  // 标记出1 - n的路径,tt是该路径消耗的时间
        if (tt > t)
        {
            cout<<"Human beings die in pursuit of wealth, and birds die in pursuit of food!"<<endl;
            continue;
        }
 
        for (int i = 1; i <= n; i++)  // 以路径上的每个节点做树形DP
            if (mark[i])              // dp[u][i]表示节点从u经过i时间回到u能获得的最大价值
                DFS(i, -1, t - tt);
 
        int DP[510], tmp[510], Max = t - tt;
        memset(DP, -1, sizeof(DP));
        DP[0] = 0;
        for (int i = 1; i <= n; i++)  // 1 - n路径上的每个点都可以绕一圈再回到原点,以获得更大价值
        {
            if (mark[i] == 0) continue;
            for (int j = 0; j <= Max; j++)
                tmp[j] = DP[j];
            for (int j = 0; j <= Max; j++)
            {
                if (dp[i][j] == -1) continue;
                for (int k = 0; k <= Max; k++)
                    if (tmp[k] != -1 && j + k <= Max)
                        DP[j+k] = max(DP[j+k], tmp[k] + dp[i][j]);
            }
        }
 
        int ans = 0;
        for (int i = 0; i <= Max; i++)
            ans = max(ans, DP[i]);
        cout<<ans<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值