poj1155 poj2468 树形DP专题。

【序言】树形DP一直不太会。趁着练DP的时候,写了两道树形的背包。鉴于这块知识非常不熟练,网上参考了一点题解。为了加强记忆,特写此题解。

【题目1】

TELE
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 3445 Accepted: 1781

Description

A TV-network plans to broadcast an important football match. Their network of transmitters and users can be represented as a tree. The root of the tree is a transmitter that emits the football match, the leaves of the tree are the potential users and other vertices in the tree are relays (transmitters). 
The price of transmission of a signal from one transmitter to another or to the user is given. A price of the entire broadcast is the sum of prices of all individual signal transmissions. 
Every user is ready to pay a certain amount of money to watch the match and the TV-network then decides whether or not to provide the user with the signal. 
Write a program that will find the maximal number of users able to watch the match so that the TV-network's doesn't lose money from broadcasting the match.

Input

The first line of the input file contains two integers N and M, 2 <= N <= 3000, 1 <= M <= N-1, the number of vertices in the tree and the number of potential users. 
The root of the tree is marked with the number 1, while other transmitters are numbered 2 to N-M and potential users are numbered N-M+1 to N. 
The following N-M lines contain data about the transmitters in the following form: 
K A1 C1 A2 C2 ... AK CK 
Means that a transmitter transmits the signal to K transmitters or users, every one of them described by the pair of numbers A and C, the transmitter or user's number and the cost of transmitting the signal to them. 
The last line contains the data about users, containing M integers representing respectively the price every one of them is willing to pay to watch the match.

Output

The first and the only line of the output file should contain the maximal number of users described in the above text.

Sample Input

9 6
3 2 2 3 2 9 3
2 4 2 5 2
3 6 2 7 2 8 2
4 3 3 3 1 1

Sample Output

5

Source


【大意】一棵树中有N个节点,编号是最后M个的节点是叶节点。每条边会有一个花费。你从1号点(根节点)开始,如果到达某个叶节点,你就能获得它的权值,但要付出所经过的边的花费。在你获得的利润S>=0的情况下,要求所到达的叶节点尽量的多。

【分析】用f[i][j]表示到i节点,以i为根的子树中到达j个的最大利润。输出时倒着循环枚举,如果f[1][ans]大于等于0就可行。下面研究状态转移方程。以前我一直以为这种选择最优解的题目要把多叉树转化为二叉树,后来发现其实并不用。我们依次枚举每一个孩子。f[k][now]=max(f[k][now],f[k][now-cut]+f[go][cut]-a[i].s);其中now是枚举当前我所在的根的状态,而cut则是给某个孩子的j值。而a[i].s就是边权。

【注意】①点多,建议开边表。

②运算时可能会有负数,一开始初始化时要赋成负无穷大。

【代码】

#include<stdio.h>
#include<iostream>
using namespace std;
const int maxn=3500+5;
const int INF=2100000000;
struct arr{int r,s,next;}a[maxn];
int num[maxn],ok[maxn],begin[maxn],end[maxn],f[maxn][maxn];
int n,m,x,b,c,i,j,ans,cnt;
void make_up(int u,int v,int s)
{
  a[++cnt].r=v;a[cnt].s=s;
  if (begin[u]==0) {begin[u]=cnt;end[u]=cnt;}
  else {a[end[u]].next=cnt;end[u]=cnt;}
}
void dp(int k)
{
  if (k>=n-m+1) 
  {
    f[k][1]=num[k];ok[k]=1;
    return;
  }
  int i=begin[k];
  while (i)
  {
    int go=a[i].r;
    dp(go);
    ok[k]+=ok[go];
    for (int now=ok[k];now>0;now--)
      for (int cut=0;cut<=ok[go];cut++)
        f[k][now]=max(f[k][now],f[k][now-cut]+f[go][cut]-a[i].s);
    i=a[i].next;   
  }
}
int main()
{
  scanf("%ld%ld",&n,&m);
  for (i=1;i<=n;i++)
    for (j=1;j<=m;j++)
      f[i][j]=-INF;
  for (i=1;i<=n-m;i++)
  {
    scanf("%ld",&x);
    while (x)
    {
      scanf("%ld%ld",&b,&c);
      make_up(i,b,c);x--;
    }
  }
  for (i=n-m+1;i<=n;i++)
    scanf("%ld",&num[i]);
  dp(1);
  for (ans=m;ans>=0;ans--)
    if (f[1][ans]>=0) {printf("%ld",ans);break;}
  return 0;
}


【题目2】

  

Apple Tree
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 6762 Accepted: 2242

Description

Wshxzt is a lovely girl. She likes apple very much. One day HX takes her to an apple tree. There are N nodes in the tree. Each node has an amount of apples. Wshxzt starts her happy trip at one node. She can eat up all the apples in the nodes she reaches. HX is a kind guy. He knows that eating too many can make the lovely girl become fat. So he doesn’t allow Wshxzt to go more than K steps in the tree. It costs one step when she goes from one node to another adjacent node. Wshxzt likes apple very much. So she wants to eat as many as she can. Can you tell how many apples she can eat in at most K steps.

Input

There are several test cases in the input 
Each test case contains three parts. 
The first part is two numbers N K, whose meanings we have talked about just now. We denote the nodes by 1 2 ... N. Since it is a tree, each node can reach any other in only one route. (1<=N<=100, 0<=K<=200) 
The second part contains N integers (All integers are nonnegative and not bigger than 1000). The ith number is the amount of apples in Node i. 
The third part contains N-1 line. There are two numbers A,B in each line, meaning that Node A and Node B are adjacent. 
Input will be ended by the end of file. 

Note: Wshxzt starts at Node 1.

Output

For each test case, output the maximal numbers of apples Wshxzt can eat at a line.

Sample Input

2 1 
0 11
1 2
3 2
0 1 2
1 2
1 3

Sample Output

11
2

Source

POJ Contest,Author:magicpig@ZSU

【分析】原来以为很水,用f[i][j]表示到第i个节点,走了j步的最大值。但是很快就发现这样有点问题。往上一看,原来还要再加一维,f[i][j][0]表示必须最后回到根节点,f[i][j][1]表示不必(当然也可以)回到根节点。

方程似乎很麻烦。

①0的状态:下去再上来,走两步。f[k][run+2][0]=max(f[k][run+2][0],f[go][down][0]+f[k][run-down][0]);
②1的状态
1、直接下去。f[k][run+1][1]=max(f[k][run+1][1],f[go][down][1]+f[k][run-down][0]);
2、也可以再上来。f[k][run+2][1]=max(f[k][run+2][1],f[go][down][0]+f[k][run-down][1]);

【代码】

#include<stdio.h>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=100+5;
int num[maxn],map[maxn][maxn],data[maxn],f[maxn][maxn*2][2];
bool visit[maxn];
int n,i,step,x,y;
void dp(int k)
{
  visit[k]=true;
  int i;
  for (i=0;i<=step;i++) f[k][i][0]=data[k];
  for (i=0;i<=step;i++) f[k][i][1]=data[k];
  for (i=1;i<=num[k];i++)
  {
    int go=map[k][i];
    if (visit[go]) continue;
    dp(go);
    for (int run=step;run>=0;run--)
      for (int down=0;down<=run;down++)
      {
        f[k][run+2][0]=max(f[k][run+2][0],f[go][down][0]+f[k][run-down][0]);
        f[k][run+1][1]=max(f[k][run+1][1],f[go][down][1]+f[k][run-down][0]);
        f[k][run+2][1]=max(f[k][run+2][1],f[go][down][0]+f[k][run-down][1]);
      }
  }
}
int main()
{
  while (scanf("%ld%ld",&n,&step)!=EOF)
  {
    memset(num,0,sizeof(num));
    memset(map,0,sizeof(map));
    memset(f,0,sizeof(f));
    memset(visit,0,sizeof(visit));
    for (i=1;i<=n;i++) scanf("%ld",&data[i]);
    for (i=1;i<n;i++)
    {
      scanf("%ld%ld",&x,&y);
      map[x][++num[x]]=y;map[y][++num[y]]=x;
    }
    dp(1); 
    printf("%ld\n",f[1][step][1]);
  }
  return 0;
}
【后记】细节坑死人!第二个程序原来DP的第二重循环down的终值我原来写成step了,就一直过不了~~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 给出一个$n\times m$的矩阵,每个位置上有一个非负整数,代表这个位置的海拔高度。一开始时,有一个人站在其中一个位置上。这个人可以向上、下、左、右四个方向移动,但是只能移动到海拔高度比当前位置低或者相等的位置上。一次移动只能移动一个单位长度。定义一个位置为“山顶”,当且仅当从这个位置开始移动,可以一直走到海拔高度比它低的位置上。请问,这个矩阵中最多有多少个“山顶”? 输入格式 第一行两个整数,分别表示$n$和$m$。 接下来$n$行,每行$m$个整数,表示整个矩阵。 输出格式 输出一个整数,表示最多有多少个“山顶”。 样例输入 4 4 3 2 1 4 2 3 4 3 5 6 7 8 4 5 6 7 样例输出 5 算法1 (递归dp) $O(nm)$ 对于这道题,我们可以使用递归DP来解决,用$f(i,j)$表示以$(i,j)$为起点的路径最大长度,那么最后的答案就是所有$f(i,j)$中的最大值。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码 算法2 (动态规划) $O(nm)$ 动态规划的思路与递归DP类似,只不过转移方程和实现方式有所不同。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值