UVA1407

来源:互联网 发布:知乎 脱发 编辑:程序博客网 时间:2024/06/05 03:33

一定距离走多远

题目描述:

给出n,表示有一个以0为根含有n个节点的树,每条边有一个权值,现在给出m次询问,每次询问有一个val值,要求计算在val值下的距离最多能经过多少个节点,一个节点多次移动多算一次.其中走的路程很大,点一共有500个

题解:

一共有两个重点.(1)点很少,距离很大,用dp的话要dp[u][i] = x,其中i是可以走的点的个数,而不是距离,用x来表示距离,这样之后扫一遍可以小于limit的i就可以.(2)树形dp的走点的个数一定要定两个状态:一定回来和无所谓.本题中:[0]指无所谓,[1]指必须回来.[1]的状态好转移.然而[0]要看v这个儿子边是不是要走出去不回来然后分成两种情况.

重点:

树形dp算一定距离能走多少个点

代码:

#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <cmath>#include <ctype.h>#include <limits.h>#include <cstdlib>#include <algorithm>#include <vector>#include <queue>#include <map>#include <stack>#include <set>#include <bitset>#define CLR(a) memset(a, 0, sizeof(a))#define REP(i, a, b) for(int i = a;i < b;i++)#define REP_D(i, a, b) for(int i = a;i <= b;i++)typedef long long ll;using namespace std;const int maxn = 500 + 10;const int INF = INT_MAX/2 - 10000*3;int n, dp[maxn][maxn][2];struct info{    int to, len, next;};info edge[maxn];int tot, head[maxn];void add_edge(int a, int b, int len){    edge[tot].to = a;    edge[tot].len = len;    edge[tot].next = head[b];    head[b] = tot;    tot++;}void dfs(int u, int fa){    dp[u][1][0] = 0;//初始化.并没有用dp依赖的写法    dp[u][1][1] = 0;    for(int i = 2;i <= n;i++)    {        dp[u][i][1] = INF;        dp[u][i][0] = INF;    }    for(int i = head[u];i!=-1;i = edge[i].next)    {        int v = edge[i].to, len = edge[i].len;        if(v!=fa)        {            dfs(v, u);            for(int i = n;i >= 2;i--)            {                for(int j = 1;j <= i - 1;j++)//至少走一个点                {                    dp[u][i][0] = min(dp[u][i][0], dp[u][i - j][0] + dp[v][j][1]+2*len);//这个v儿子边回来,而是其他的边走掉                    dp[u][i][0] = min(dp[u][i][0], dp[u][i - j][1] + dp[v][j][0]+len);//这个v儿子边走掉,其他的边都回来                    dp[u][i][1] = min(dp[u][i][1], dp[u][i-j][1]+dp[v][j][1]+2*len);//这个简单,都得回来                }            }        }    }}void solve(){    dfs(0, -1);    int q;    scanf("%d", &q);    while(q--)    {        int x;        int ans = 0;        scanf("%d", &x);        for(int i = 1;i <= n;i++)        {            if(dp[0][i][0] <= x)            {                ans = i;            }        }        printf("%d\n", ans);    }}int main(){   // freopen("9Iin.txt", "r", stdin);    //freopen("9Iout.txt", "w", stdout);    int ncase = 0;    while(scanf("%d", &n) != EOF)    {        if(n==0)            break;        ncase++;        printf("Case %d:\n", ncase);        memset(head, -1, sizeof(head));        tot = 0;        REP_D(i, 1, n - 1)        {            int a, b, len;            scanf("%d%d%d", &a, &b, &len);            add_edge(a, b, len);        }        solve();    }    return 0;}
0 0