专题传送门:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=24217#overview
A.POJ 1155
/*
类型:树形DP
题意:广播一场比赛,有发射站和用户,用户为叶子节点。每个发射站有消耗,到达用户有收益,现在问在不赔钱的情况下怎样使用户最大
思路:dp[i][j] 为给第i个节点分配k个用户的收益(可以为负),显然使dp[1][j]为正的最大j为所求
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>
#define maxn 3100
#define maxm 1100
#define INF (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))
using namespace std;
inline TP Max(TP a,TP b)
{
if (a > b) return a;
return b;
}
inline TP Min(TP a,TP b)
{
if (a > b) return b;
return a;
}
bool cmp(TP a,TP b)
{
return a < b;
}
//BEGIN
struct EDGE{
int v,w;
};
bool cmp1(EDGE a,EDGE b)
{
return a.w > b.w;
}
int dp[maxn][maxn],
val[maxn],
son[maxn];
vector <EDGE> links[maxn];
int dfs1(int k)
{
int i,mm;
mm = links[k].size();
if (mm == 0) son[k] = 1;
else son[k] = 0;
for(i = 0;i < mm;i++)
son[k] += dfs1(links[k][i].v);
return son[k];
}
void dfs2(int k)
{
//printf("%dxxx\n",k);
int mm = links[k].size(),
i,ii,jj,now_limt = 0;
dp[k][0] = 0;
if (mm == 0) {
dp[k][1] = val[k];
}
for(i = 0;i < mm;i++)
dfs2(links[k][i].v);
for(i = 0;i < mm;i++) //分组背包,枚举组
for(ii = (now_limt += son[links[k][i].v]);ii >= 1;ii--)
for(jj = 1;jj <= ii && jj <= son[links[k][i].v];jj++)
dp[k][ii] = Max(dp[k][ii],dp[k][ii-jj] + dp[links[k][i].v][jj] - links[k][i].w);
//
//printf("%d:\n",k);for(i = 1;i <= son[k];i++) printf("%d ",dp[k][i]);printf("\n");
return ;
}
int Work()
{
int n,m,tran,i,j,t;
EDGE tmp;
scanf("%d%d",&n,&m);
for(i = 0;i < maxn;i++)
for(j = 0;j < maxn;j++)
dp[i][j] = -INF;
Clear(son,0);
tran = n - m;
for(i = 1;i <= tran;i++) {
scanf("%d",&t);
for(j = 1;j <= t;j++){
scanf("%d%d",&tmp.v,&tmp.w);
links[i].push_back(tmp);
}
//sort(links[i].begin(),links[i].end(),cmp1);
}
for(;i <= n;i++)
scanf("%d",&val[i]);
dfs1(1); //第一遍DFS统计每个点的子树的点数
//for(i = 1;i <= n;i++)
// printf("%d_%d\n",i,son[i]);
dfs2(1); //第二遍DFS开始DP
// for(i = 1;i <= m;i++)
// printf("%d %d\n",i,dp[1][i]);
int ans = m;
while(dp[1][ans] < 0) ans--;
printf("%d\n",ans);
return 0;
}
int main()
{
//freopen("test.in","r",stdin);
Work();
return 0;
}
B. HDU 1011
/*
类型:树形DP
题意:一个虫子巢穴为树形结构,每个房间都有一些虫子和一定的概率存在脑虫。现在给你一定的战士,1战士可以消灭20个虫子,让你使打到脑虫的可能性最大
思路:dp[i][j]代表打i子树要j战士时的最大可能
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>
#define maxn 210
#define maxm 110
#define INF (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))
using namespace std;
inline TP Max(TP a,TP b)
{
if (a > b) return a;
return b;
}
inline TP Min(TP a,TP b)
{
if (a > b) return b;
return a;
}
bool cmp(TP a,TP b)
{
return a < b;
}
//BEGIN
int m;
int dp[maxn][maxn],bug[maxn],sum_bug[maxn],boss[maxn];
vector <int> edge[maxn];
void dfs2(int k,int fa)
{
//printf("%d\n",k);
int mm = edge[k].size(),i,ii,jj;
for(i = 0;i < mm;i++)
if (edge[k][i] != fa)
dfs2(edge[k][i],k);
//if ((mm == 1 && fa > 0) || mm == 0)
// if (bug[k] <= m) dp[k][bug[k]] = boss[k];
for(i = bug[k];i <= m;i++) dp[k][i] = boss[k];
for(i = 0;i < mm;i++)
if (edge[k][i] != fa){
for(ii = m;ii >= bug[k];ii--)
for(jj = 1;jj <= ii-bug[k];jj++)
dp[k][ii] = Max(dp[k][ii],dp[k][ii-jj] + dp[edge[k][i]][jj]);
}
//printf("%d: %d\n",k,boss[k]);
//for(i = 0;i <= sum_bug[k];i++) printf("%d ",dp[k][i]);printf("\n");
return ;
}
int Work()
{
int n,i,tmp_u,tmp_v;
scanf("%d%d",&n,&m);
if (n == -1) return 0;
for(i = 1;i <= n;i++)
edge[i].clear();
Clear(dp,0);
Clear(sum_bug,0);
Clear(bug,0);
for(i = 1;i <= n;i++)
scanf("%d%d",&bug[i],&boss[i]),bug[i] = (bug[i] + 19) / 20;
//for(i = 1;i <= n;i++) printf("%d ",bug[i]);printf("\n");
for(i = 1;i < n;i++){
scanf("%d%d",&tmp_u,&tmp_v);
edge[tmp_u].push_back(tmp_v);
edge[tmp_v].push_back(tmp_u);
}
if (m == 0) {
printf("0\n");
return 1;
}
dfs2(1,0);
printf("%d\n",dp[1][m]);
return 1;
}
int main()
{
//freopen("test.in","r",stdin);
while(Work());
return 0;
}
//END
C. POJ 1947
/*
类型:树形DP
题意:给出一棵树,问最少切断几条边可以得到有p个结点的子树
思路:dp[i][j]代表第i个点为根的子树要得到j个节点的子树最少切多少个边
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>
#define maxn 200
#define maxm 1100
#define INF (1000000)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))
using namespace std;
inline TP Max(TP a,TP b)
{
if (a > b) return a;
return b;
}
inline TP Min(TP a,TP b)
{
if (a > b) return b;
return a;
}
bool cmp(TP a,TP b)
{
return a < b;
}
//BEGIN
int p,ans;
int dp[maxn][maxn];
vector <int> edge[maxn];
void dfs(int k)
{
int mm = edge[k].size(),
i,ii,jj;
if (k == 1) dp[k][1] = mm;
else dp[k][1] = mm + 1;
dp[k][0] = 1;
for(i = 0;i < mm;i++) {
dfs(edge[k][i]);
for(ii = p;ii >= 1;ii--)
for(jj = 1;jj <= ii-1;jj++)
dp[k][ii] = Min(dp[k][ii],dp[k][ii-jj] + dp[edge[k][i]][jj] - 2);
}
//printf("%3d:",k);for(i = 0;i <= p;i++) printf("%9d",dp[k][i]);printf("\n");
if (dp[k][p] < ans) ans = dp[k][p];
return ;
}
int Work()
{
int n,i,fa,son,j;
scanf("%d%d",&n,&p);
for(i = 0;i <= n;i++)
for(j = 0;j <= p;j++)
dp[i][j] = INF;
for(i = 1;i < n;i++) {
scanf("%d%d",&fa,&son);
edge[fa].push_back(son);
}
ans = INF;
dfs(1);
printf("%d\n",ans);
return 0;
}
int main()
{
//freopen("test.in","r",stdin);
Work();
return 0;
}
//END
D. HDU 1561
/*
类型:树形DP
题意:取M个城堡,使那啥最大
思路:不说了,桑心
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>
#define maxn 250
#define maxm 1100
#define INF (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))
using namespace std;
inline TP Max(TP a,TP b)
{
if (a > b) return a;
return b;
}
inline TP Min(TP a,TP b)
{
if (a > b) return b;
return a;
}
bool cmp(TP a,TP b)
{
return a < b;
}
//BEGIN
int n,m;
int dp[maxn][maxn],val[maxn];
vector <int> son[maxn];
void dfs(int k)
{
int mm = son[k].size(),
i,ii,jj;
dp[k][1] = val[k];
for(i = 0;i < mm;i++) {
dfs(son[k][i]);
for(ii = m;ii >= 1;ii--)
for(jj = 0;jj <= ii-1;jj++)
dp[k][ii] = Max(dp[k][ii],dp[k][ii-jj] + dp[son[k][i]][jj]);
}
//printf("%d: ",k);for(i = 0;i <= n;i++) printf("%d ",dp[k][i]);printf("\n");
return ;
}
int Work()
{
int a,b,i;
scanf("%d%d",&n,&m);
if (n == 0) return 0;
m++;
Clear(dp,0);
Clear(val,0);
for(i = 0;i <= n;i++)
son[i].clear();
for(i= 1;i <= n;i++){
scanf("%d%d",&a,&val[i]);
son[a].push_back(i);
}
//for(i = 1;i <= n;i++) printf("%d ",val[i]);printf("\n");
dfs(0);
printf("%d\n",dp[0][m]);
return 1;
}
int main()
{
// freopen("test.in","r",stdin);
while(Work());
return 0;
}
//END
E. HDU 1561
/*
类型:树形DP
题意:给出一棵树,投放K个机器人,问使全部点都被走过的情况下机器人走的总路程和最小。
思路:dp[i][j]代表第i个点为根的子树要得到j个节点的子树最少切多少个边
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>
#define maxn 11000
#define maxm 12
#define INF (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))
using namespace std;
inline TP Max(TP a,TP b)
{
if (a > b) return a;
return b;
}
inline TP Min(TP a,TP b)
{
if (a > b) return b;
return a;
}
bool cmp(TP a,TP b)
{
return a < b;
}
//BEGIN
struct EDGE{
int ed,w;
} tmp;
int n,k;
int dp[maxn][maxm];
vector <EDGE> edge[maxn];
void dfs(int now,int fa)
{
int mm = edge[now].size(),
i,ii,jj,next;
for(i = 0;i < mm;i++)
if (edge[now][i].ed != fa) {
next = edge[now][i].ed;
dfs(next,now);
for(ii = k;ii >= 0;ii--) {
dp[now][ii] += dp[next][0] + edge[now][i].w * 2;
for(jj = 0;jj <= ii;jj++)
dp[now][ii] = Min(dp[now][ii],dp[now][ii-jj] + dp[next][jj] + edge[now][i].w * jj);
}
}
return ;
}
int Work()
{
int i,j,s,u,v,w;
if (scanf("%d%d%d",&n,&s,&k) == EOF) return 0;
for(i = 1;i <= n;i++) edge[i].clear();
Clear(dp,0);
for(i = 1;i < n;i++) {
scanf("%d%d%d",&u,&v,&w);
tmp.w = w;
tmp.ed = v;
edge[u].push_back(tmp);
tmp.ed = u;
edge[v].push_back(tmp);
}
dfs(s,0);
printf("%d\n",dp[s][k]);
return 1;
}
int main()
{
//freopen("test.in","r",stdin);
while(Work());
return 0;
}
F. POJ 2486
/*
树形DP
题意:一颗树上吃苹果,每个点上都有一定的苹果
思路:状态dp[i][j][k]表示为第i个点走j步,k为0代表不回,k为1代表回则有三种转移,见代码。
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>
#include<math.h>
#define maxn 110
#define maxm 210
#define INF (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))
using namespace std;
inline TP Max(TP a,TP b)
{
if (a > b) return a;
return b;
}
inline TP Min(TP a,TP b)
{
if (a > b) return b;
return a;
}
bool cmp(TP a,TP b)
{
return a < b;
}
//BEGIN
int k,n;
int apple[maxn],
dp[maxn][maxm][2];
vector<int>edge[maxn];
void dfs(int x,int fa)
{
int mm,i,j,jj,son;
//0-不回 1-回
mm = edge[x].size();
for(i = 0;i < mm;i++)
if (edge[x][i] != fa)
dfs(edge[x][i],x);
for (j = 0;j <= k;j++)
dp[x][j][1] = dp[x][j][0] = apple[x];
for(i = 0;i < mm;i++)
if ((son = edge[x][i]) != fa)
for(j = k;j >= 0;j--) //又他妈傻逼了!!!!!!
for(jj = 0;jj <= j;jj++) {
if (jj >= 2) dp[x][j][0] = Max(dp[x][j][0],dp[x][j-jj][0]+dp[son][jj-2][1]);
if (jj >= 1) dp[x][j][0] = Max(dp[x][j][0],dp[x][j-jj][1]+dp[son][jj-1][0]);
if (jj >= 2) dp[x][j][1] = Max(dp[x][j][1],dp[x][j-jj][1]+dp[son][jj-2][1]);
}
return ;
}
int Work()
{
int i,u,v;
if (scanf("%d%d",&n,&k) == EOF) return 0;
Clear(dp,0);
Clear(apple,0);
for(i = 1;i <= n;i++) scanf("%d",&apple[i]);
for(i = 1;i <= n;i++) edge[i].clear();
for(i = 1;i < n;i++) {
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs(1,0);
printf("%d\n",dp[1][k][0]);
}
int main()
{
//freopen("test.in","r",stdin);
while(Work());
return 0;
}
//END
J. HDU 4276
/*
树形DP
题意:
思路:
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<algorithm>
#include<math.h>
#define maxn 110
#define maxm 510
#define OO (1 << 28)
#define TP int
#define LL long long
#define DB double
#define Mid(x) ((x) >> 1)
#define Lc(x) ((x) << 1)
#define Rc(x) (((x) << 1)|1)
#define Clear(a,b) memset(a,b,sizeof(a))
using namespace std;
inline TP Max(TP a,TP b)
{
if (a > b) return a;
return b;
}
inline TP Min(TP a,TP b)
{
if (a > b) return b;
return a;
}
bool cmp(TP a,TP b)
{
return a < b;
}
//BEGIN
struct EDGE{
int ed,cost;
} tmp;
vector<EDGE>edge[maxn];
int n,t;
int dp[maxn][maxm],gold[maxn];
int dfs_find(int now,int fa)
{
int mm,i,time;
if (now == n) return 0;
mm = edge[now].size();
for(i = 0;i < mm;i++)
if (edge[now][i].ed != fa) {
time = dfs_find(edge[now][i].ed,now) + edge[now][i].cost;
if (time >= 0) {
edge[now][i].cost = 0;
return time;
}
}
return -OO;
}
void dfs(int now,int fa)
{
int mm,i,son,cost,j,jj;
mm = edge[now].size();
for(j = 0;j <= t;j++) dp[now][j] = gold[now];
for(i = 0;i < mm;i++)
if (edge[now][i].ed != fa) {
son = edge[now][i].ed;
dfs(son,now);
cost = edge[now][i].cost;
for(j = t;j >= 0;j--)
for(jj = cost*2;jj <= j;jj++)
dp[now][j] = Max(dp[now][j] , dp[now][j-jj] + dp[son][jj-cost*2]);
}
return ;
}
int Work()
{
int i,u,v,w;
if (scanf("%d%d",&n,&t) == EOF) return 0;
Clear(dp,0);
Clear(gold,0);
for(i = 1;i <= n;i++)
edge[i].clear();
for(i = 1;i < n;i++) {
scanf("%d%d%d",&u,&v,&w);
tmp.cost = w;
tmp.ed = u;
edge[v].push_back(tmp);
tmp.ed = v;
edge[u].push_back(tmp);
}
for(i = 1;i <= n;i++)
scanf("%d",&gold[i]);
t -= dfs_find(1,0);
//printf("%dXXX\n",t);
if (t <= 0) {
printf("Human beings die in pursuit of wealth, and birds die in pursuit of food!\n");
return 1;
}
dfs(1,0);
printf("%d\n",dp[1][t]);
return 1;
}
int main()
{
//freopen("test.in","r",stdin);
while(Work());
return 0;
}
//END