版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/85121058
题目一:Anniversary party
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1520
题意:有n个客人,每个客人有一个开心值,然后客人K是客人L的主管,客人K和客人L不能一起被邀请参加party,问:邀请人的最大开心值是多少?
题解:显然这是道树形dp 的题,我们定义 dp[i][1]表示邀请客人i,dp[i][0]表示不邀请客人i,
故转移方程为:
dp[i][1]+=d[j][0] ,
dp[i][0]+=max(dp[j][1],dp[j][0])。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=6010;
int dp[maxn][2];
vector<int> G[maxn];
int fa[maxn];
int dfs(int root)
{
int len=G[root].size();
for(int i=0;i<len;i++) ///一直递归到叶子节点,返回上一层开始
dfs(G[root][i]);
for(int i=0;i<len;i++)
{
dp[root][1]+=dp[G[root][i]][0];
dp[root][0]+=max(dp[G[root][i]][0],dp[G[root][i]][1]);
}
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
{
scanf("%d",&dp[i][1]); ///表示邀请客人i的开心值
dp[i][0]=0;
G[i].clear();
fa[i]=-1;
}
int L,K;
while(scanf("%d%d",&L,&K))
{
if(L==0&&K==0) break;
///在这里我们就不建立双向边了,因为我们是从最深一层开始往上dp,故每个节点都会只搜一次
G[K].push_back(L);
fa[L]=K; ///
}
int root=1; ///找到根节点
while(fa[root]!=-1) root=fa[root];
dfs(root);
printf("%d\n",max(dp[root][1],dp[root][0]));
}
return 0;
}
题目二:Strategic Game
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1054
题意:给你一个n个节点的树,让你挑选出一些节点放士兵,使得所有边都能被士兵监视,让你尽可能的少放士兵。
题解:
显然这道题跟上一道类似,也可以用树形dp来解决,还是一样dp[i][1]表示挑选节点i放士兵,dp[i][0]表示不挑选节点i放士兵,
转移方程为:
dp[i][0]+=d[j][1] ,
dp[i][1]+=min(dp[j][1],dp[j][0])。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=1510;
int dp[maxn][2];
vector<int> G[maxn];
int fa[maxn];
int dfs(int root)
{
// printf("f");
int len=G[root].size();
for(int i=0;i<len;i++) ///一直递归到叶子节点,返回上一层开始
dfs(G[root][i]);
for(int i=0;i<len;i++)
{
dp[root][0]+=dp[G[root][i]][1];
dp[root][1]+=min(dp[G[root][i]][0],dp[G[root][i]][1]);
}
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)
{
dp[i][1]=1;///表示邀请客人i的开心值
dp[i][0]=0;
G[i].clear();
fa[i]=-1;
}
int root,rootlen,son;
for(int i=0;i<n;i++)
{
scanf("%d:(%d)",&root,&rootlen);
while(rootlen--){
scanf("%d",&son);
///在这里我们就不建立双向边了,因为我们是从最深一层开始往上dp,故每个节点都会只搜一次
G[root].push_back(son);
fa[son]=root;
}
}
root=0; ///找到根节点
while(fa[root]!=-1) root=fa[root];
// printf("f");
dfs(root);
printf("%d\n",min(dp[root][1],dp[root][0]));
}
return 0;
}