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;
}