題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=4276)
參考鏈接:http://blog.csdn.net/u012841845/article/details/18739425
以及:http://blog.csdn.net/xianxingwuguan1/article/details/18954537
手寫鄰接表:http://blog.csdn.net/ooooooooe/article/details/17035501
分析:先尋找1到N的最短路徑和走這段路所用的時間,再把路徑的權置為0,如果時間有多的,就把剩余的時間拿來進行樹上背包,具體過程我已經寫在了程序的注釋里,之所以要寫這個,是因為網上關于這道題的文章雖然很多,但都講解得不是很清楚。
head數組是一種手寫鄰接表的方法,在上面的網址可以找到,但是該博并沒有說明,所以我特地去請教了另外的人。它是圖的另一種存儲方法,head[a]表示以a為起點的邊的編號,下面的add函數中的head[u] = tol++是在更新編號(即改為當前邊的編號),表面上看起來它是在一直變化,但是每一次add它都會把自己的指存儲在的edge結構體中,這樣就可以根據一個head值一直找到它的父親的父親的父親......另外,之所以說最短路上的邊指走一遍是因為財寶只有那么多,拿了一次就沒有了。
include <cstdio>
include <cstring>
include <iostream>
using namespace std;
const int maxn = 200;
int head[maxn], tol, dpmaxn, weight[maxn], T, t, n;
// 此結構體表示各條邊(這里其實是把room當做是有向無環圖來看待,
// 每一條邊都以兩個方向來表示,該結構體的元素含義如下:
// next:表示下一條以當前邊為起點的邊(和鄰接表的實現有些類似)
// to:表示這條邊的終點
// time:表示走這條邊所需要的時間
// 另外兩個就是默認構造函數和構造函數了
struct node
{
int next, to, time;
node(){};
node(int next, int _to, int _time) : next(next), to(to), time(time){}
}edge[5*maxn];
// add函數用來添加邊(每條邊用edge結構體表示)
void add(int u, int v, int time)
{
edge[tol] = node(head[u], v, time);
head[u] = tol++;
}
// 深度搜索找到1到N的最短路徑(即花費時間最少)
// 并把該路徑上的所有邊所要的時間置為0,方便之后搜索,并得到最短時間為t
// 其中u表示當前節點,pre表示上一個節點
bool dfs(int u, int pre)
{
if(u == n)
return 1;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if( v == pre )
continue;
if(dfs(v, u)) // 當返回1時就表示采取了那一條邊
{
t += edge[i].time;
edge[i].time = 0;
return 1;
}
}
return 0;
}
// 深度搜索,統計了所有節點在一定時間范圍內所有時間所能獲得的最大價值
// dpu:表示從u點出發到回到u點,花費時間j所能獲得的最大的財富
void dfs1(int u, int pre)
{
for(int i = 0; i <= T; i++) // T為剩下的總時間
dpu = weight[u]; // 只要是經過了這個節點的都應該把它的財寶即weight加起來
for(int i = head[u]; i != -1; i = edge[i].next) // 這就是上面使用鄰接表的作用,方便深度搜索
{
int v = edge[i].to;
if( v == pre ) // 如果是指向上一條邊的就直接略過
continue;
dfs1(v, u); // 遞歸
int pp = 2*edge[i].time; // 與u點直接相連的那一條邊的時間,因為如果要繼續下去,就必然會經過該邊
for(int j = T; j >= pp; j–) // j必須大于走那條邊的時間,它表示的是從該邊走的總時間
for(int k = 0; k <= j-pp; k++) // k表示走了那條邊后從那條邊的終點繼續走所用的時間
dpu = max(dpu, dpv+dpu); // j-pp-k表示剩下的從u點出發走的時間
}
}
int main()
{
int i, j, k, p;
while(~scanf(“\%d\%d”, &n, &T))
{
memset(head, -1, sizeof(head));
tol = 0;
for(i = 1; i < n; i++)
{
scanf(“\%d\%d\%d”, &j, &k, &p);
add(j, k, p);
add(k, j, p);
}
for(i = 1; i <= n; i++)
scanf(“\%d”, &weight[i]);
t = 0;
dfs(1, 1);
if(t > T)
{
puts(“Human beings die in pursuit of wealth, and birds die in pursuit of food!”);
continue;
}
memset(dp, 0, sizeof(dp));
T -= t;
dfs1(1, -1);
cout<< dp1 <<endl; // 應該無論是否從1出發都能得到同樣的結果的
}
return 0;
}
這道題花了兩天時間,寫了七八張紙都沒有完全理解清楚,難道真的是我太愚笨了嗎?