题目详情:
羊村新建了许多地下房子,这些地下房子的分布成树状结构。每一个房子有一个编号,从0—n-1,0号是村长的房子。注意每间房子只住着一只羊。
村长的房子是其他所有房子的入口(即根节点),之后每个房子最多通向其他两个房子。房子与房子之间有不同的距离。
灰太狼听从他老婆的命令,去抓羊。灰太狼总是每秒前行1米,这导致他有时候不能完成他老婆给他的任务。他老婆会有很多次命令,即给他不同的时间(从它的住处出发,抓完羊后再回到住处的总时间)和要抓的羊的数量,你的任务是判断灰太狼能不能完成任务。
注意:由于抓狼行动在夜晚,所有的羊都睡着了,所以只要灰太狼进入到羊的房间就一定会抓住羊。
输入描述:
第一行给出三个整数n(1<n<=500),q(0<q<=100000),d,分别表示羊村的房间数目,灰太狼的任务数,灰太狼的住处到羊村的距离(m)。
接下来n-1行,每行输入三个整数a,b,c,表示房间a到房间b的距离是c米。
接着是q行,每行两个整数t,m,分别表示t秒,m(m>0)只羊,即灰太狼的老婆给他的任务。
有多组测试数据当n=0时输入结束。
输出描述:
输出q行。对于每个任务,如果灰太狼有可能完成就输出“I love you”,否则输出“Go to the hell”。
答题说明:
样例输入:
8 8 1
0 1 4
0 2 5
1 3 7
1 4 2
4 6 3
4 7 1
2 5 9
10 2
13 3
22 5
62 8
65 8
0 1
46 7
32 6
0
样例输出:
I love you
Go to the hell
I love you
Go to the hell
I love you
Go to the hell
I love you
I love you
这个题我第一眼看时没什么头绪,那是它刚出出来时了,今天它跑到题目列表的前面去了,我又看了一下,联想到DP上去了,于是就会了,神奇的DP。
首先把一颗子树看成一个子解,也就是从这颗子树内选出来一条关于选取节点数量n的最小路径。于是对于某根节点,其选取过程就只依赖于它的两个子节点了。有dp[x][n] = min(dp[x.leftchild][i] + dp[x.rightchild][n - i])(0 <= i <= n)
这样这个题目就有眉目了。不过接下来才是这次做题的艰难时刻……自己在思维方面还是比较欠缺,花了两个多小时才把代码打出来。对于子树终止条件一直没捋清楚,导致各种BUG。这些都显现出我思路还是不够敏锐……这个要怎么提升啊!
#include<stdio.h>
#include<string.h>
#define N 550
#define INF 0x0fffffff
int n, q, d;
//连接矩阵
int map[N][N];
//足迹
int mark[N];
//子树表
int next[N][2];
//从父节点到本节点的消耗
int vs[N];
int dp[N][N];
//递归建立从属关系
void build(int root){
int i, j;
mark[root] = 1;
for(i = j = 0; i < n && j < 2; i++){
if(map[root][i] >= 0 && mark[i] == 0){
next[root][j] = i;
vs[i] = map[root][i];
build(i);
j++;
}
}
}
//建立dp表
void run(int root){
int i, j, c1, c2;
if(root < 0){//叶子的子节点,当递归run时,会走到这里
return;
}
c1 = next[root][0], c2 = next[root][1];//两子节点
run(c1);//先建立子dp表
run(c2);
dp[root][1] = vs[root] * 2;//仅取根节点
if(c1 > 0 && c2 > 0){//双子都在
for(i = 1; i + 1 <= n; i++){
int min = INF;
for(j = 0; j <= i; j++){
int v1 = c1 > 0 ? dp[c1][j] : INF;//INF表示不可选
int v2 = c2 > 0 ? dp[c2][i - j] : INF;
if(v1 + v2 < min){
min = v1 + v2;
}
}
dp[root][i + 1] = min + vs[root] * 2;
}
} else if(c1 > 0){
for(i = 1; i + 1 <= n; i++){
dp[root][i + 1] = dp[c1][i] + vs[root] * 2;
}
} else if(c2 > 0){
for(i = 1; i + 1 <= n; i++){
dp[root][i + 1] = dp[c2][i] + vs[root] * 2;
}
}
}
int main(void){
int i, j;
while(scanf("%d%d%d", &n, &q, &d), n != 0){
//初始化
memset(mark, 0, sizeof(mark));
for(i = 0; i < N; i++){
next[i][0] = next[i][1] = -1;
for(j = 0; j < N; j++){
map[i][j] = -1;
dp[i][j] = INF;
}
}
for(i = 0; i < N; i++){
dp[i][0] = 0;
}
for(i = 1; i < n; i++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
map[a][b] = map[b][a] = c;
}
build(0);
vs[0] = d;//从家到0节点
run(0);
for(i = 0; i < q; i++){
int t, m;
scanf("%d%d", &t, &m);
printf(dp[0][m] <= t ? "I love you\n" : "Go to the hell\n");
}
}
return 0;
}
///更新
第一次做这个题时写出了上边的代码,并且通过了。过了几天发现再提交居然判失败。先在自己这里找问题,仔细地推敲了代码,结果再也没通过过……不知道是我的思维死区还是CSDN在卖萌,根据历史经验,我认为是后者。
不过还是把代码贴出来,有没有眼尖的能帮帮忙?
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef unsigned long long llu;
const llu Inf = 0xffffffffffffffff;
const llu N = 550;
llu n, q, d;
llu map[N][N];
bool pass[N][N];
llu cost[N];
llu dp[N][N];
bool used[N];
int ord[N];
vector<int> ch[N];
int main(void){
while(cin >> n, n != 0){
cin >> q >> d;
fill((bool *)pass, (bool *)pass + N * N, false);
fill((llu *)dp, (llu *)dp + N * N, Inf);
fill(used, used + n, false);
for(int i = 1; i < n; i++){
int a, b;
llu c;
cin >> a >> b >> c;
map[a][b] = map[b][a] = c;
pass[a][b] = pass[b][a] = true;
}
queue<int> qu;
qu.push(0);
int orp = 0;
cost[0] = d;
while(!qu.empty()){
int v = qu.front(); qu.pop();
used[v] = true;
ord[orp++] = v;
for(int i = 0; i < n; i++){
if(pass[v][i] && !used[i]){
qu.push(i);
ch[v].push_back(i);
cost[i] = map[v][i];
}
}
}
for(int i = n - 1; i >= 0; i--){
int v = ord[i];
dp[v][0] = 0;
dp[v][1] = cost[v];
if(ch[v].size() == 1){
int u = ch[v][0];
for(int i = 2; i < n; i++){
dp[v][i] = dp[u][i - 1] < Inf ? dp[u][i - 1] + cost[i] : Inf;
}
} else if(ch[v].size() == 2){
int u = ch[v][0], w = ch[v][1];
for(int i = 2; i <= n; i++){
llu mn = Inf;
for(int j = 0; j < i; j++){
if(dp[u][j] < Inf && dp[w][i - 1 - j] < Inf){
mn = min(mn, dp[u][j] + dp[w][i - 1 - j]);
}
}
dp[v][i] = mn < Inf ? mn + cost[v] : Inf;
}
}
}
for(int i = 0; i < q; i++){
llu t, m;
cin >> t >> m;
cout << (dp[0][m] * 2 <= t ? "I love you\n" : "Go to the hell\n");
}
}
return 0;
}