pku 树形dp 1849 two 解题报告

 

pku 1849 two 解题报告

题目链接: http://acm.pku.edu.cn/JudgeOnline/problem?id=1849

题意:

给出一棵树的公路网,公路网堆满雪,需要2辆汽车扫完全部雪,树的权值表示耗费汽车的油费,求汽车从结点S开始,用最少汽油费扫完全部雪,汽油费是多少?

思路:

通过此题,更加深刻了解树形dp的特点,树形dp通常是典型的从低向上dp,解决数据大,是树形结构的。解决问题的关键在于寻找其状态方程,然后用深度搜索搜到树低,然后利用状态转移方程一步一步往上dp

     由于题目只是要求2辆汽车,那么我们想想,如果S只有一棵子树,出现的情况可能就是1辆汽车走到底或者2辆汽车走到底.再深入一步想,有两棵子树呢?就应该2辆汽车各走一个子树。3棵子树呢?就应该有1可子树是最短的而且走2,以此类推456```棵子树的情况,只要先求出最短2棵子树的路径,那么所有子树的路径之和*2-2棵最短路径子树之和即为答案.那么状态转移方程就出来了: ji的儿子

dp[i][1]=2*j->Distance+dp[j][1]      //1辆汽车去一棵子树了又返回其结点

dp[i][2]= min(dp[i][1]+ dp[j][2] - dp[j][1] - j->Distance)   //1辆汽车去了子树不返回其结点

dp[i][3]=min(dp[i][1] +dp[j][3] - dp[j][1]- LongestPath - MinisterPath)//2辆汽车去了子树都不返回其结点

 

AC代码:

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#define M 100005

 

typedef struct node

{

       int Point, Distance;

       struct node *next;

}*adjv;

adjv tree[M];

int N, S;

bool visit[M];             //对结点的访问

int dp[M][3];              //只需要记录三条路径 1:1--->1(1人去同时此人回到原点), 2:1---->0(1人去同时此人不回来), 3:2---->0(2人去同时2人都不回来)

 

void input()

{

       int i, a, b, c;

 

       for (i = 1; i <= N; i++)

       {

              tree[i] = NULL;

       }

       for (i = 1; i < N; i++)

       {

              //用链接表保存这棵树

              scanf("%d%d%d", &a, &b, &c);

              node *temp = new node;

              temp->Point = b; temp->Distance = c; temp->next = NULL;

              if (tree[a] == NULL)

              {

                     tree[a] = temp;

              }

              else

              {

                     node *tmp = tree[a];

                     while (tmp->next)

                     {

                            tmp = tmp->next;

                     }

                     tmp->next = temp;

              }

              temp = new node;

              temp->Point = a; temp->Distance = c; temp->next = NULL;

              if (tree[b] == NULL)

              {

                     tree[b] = temp;

              }

              else

              {

                     node *tmp = tree[b];

                     while (tmp->next)

                     {

                            tmp = tmp->next;

                     }

                     tmp->next = temp;

              }

       }

}

 

//   dp方程是dp[k][i][j]表示以第k个点为子树的去了i个人回来j个人的最小花费

//   这样需要对3i,j的值进行计算,不难看出是1 0, 1 1, 2 0,

void dfs(int point)

{

       visit[point] = true;

       int SavePath[M];

       node *temp = tree[point];

       //一人去一人返回

       dp[point][1] = 0;

       int i, min1 = 0, min2 = 0, count = 0;

       while (temp)

       {

              if (!visit[temp->Point])

              {

                     //深度搜索搜索到树低

                     dfs(temp->Point);

                     //1----->1  路径*2+儿子回来的路径

                     dp[point][1] += 2 * temp->Distance + dp[temp->Point][1];

                     //画图可知,求出point1人回来与1人不回来的差值

                     if (min1 > dp[temp->Point][2] - dp[temp->Point][1] - temp->Distance)

                     {

                            min1 = dp[temp->Point][2] - dp[temp->Point][1] - temp->Distance;

                     }

                     //画图可知,求出point2人回来与2人不回来的差值

                     if (min2 > dp[temp->Point][3] - dp[temp->Point][1])

                     {

                            min2 = dp[temp->Point][3] - dp[temp->Point][1];

                     }

                     SavePath[count++] = dp[temp->Point][2] - dp[temp->Point][1] - temp->Distance;

              }

              temp = temp->next;

       }

       //1人不回来的数值

       dp[point][2] = dp[point][1] + min1;

       int LongestPath = 0, MinisterPath = 0;

       //如果儿子大于2的话,即子树>=2,那么最佳路径就是所有子树路径*2-2条最长路径,2人也不用回来.

       if (count > 1)

       {

              for (i = 0; i < count; i++)

              {

                     if (SavePath[i] < LongestPath)

                     {

                            MinisterPath = LongestPath;

                            LongestPath = SavePath[i];

                     }

                     else if (SavePath[i] < MinisterPath)

                     {

                            MinisterPath = SavePath[i];

                     }

              }

              min2 = ((LongestPath + MinisterPath) < min2 ? (LongestPath + MinisterPath) : min2);

       }

       dp[point][3] = dp[point][1] + min2;

}

 

int min(int a, int b, int c)

{

       int temp = a > b ? b : a;

       return temp > c ? c : temp;

}

 

int main()

{

       //freopen("1.txt", "r", stdin);

       scanf("%d%d", &N, &S);

       input();

       dfs(S);

       //答案取3种情况的最优值

       int ans = min(dp[S][1], dp[S][2], dp[S][3]);

       printf("%d/n", ans);

       return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值