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

Croatia OI 2002 Final Exam - Second Day

【大意】一棵树中有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了,就一直过不了~~

你可能感兴趣的:(poj1155 poj2468 树形DP专题。)