『战略游戏 最大利润 树形DP』

通过两道简单的例题,我们来重新认识树形DP。


战略游戏(luoguP1026)

Description

Bob喜欢玩电脑游戏,特别是战略游戏。但是他经常无法找到快速玩过游戏的办法。现在他有个问题。他要建立一个古城堡,城堡中的路形成一棵树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。

请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵。

Input Format

输入文件中数据表示一棵树,描述如下: 第一行 N,表示树中结点的数目。 第二行至第N+1行,每行描述每个结点信息,依次为:该结点标号i,k(后面有k条边与结点I相连),接下来k个数,分别是每条边的另一个结点标号r1,r2,...,rk。 对于一个n(0<n<=1500)个结点的树,结点标号在0到n-1之间,在输入文件中每条边只出现一次。

Output Format

输出文件仅包含一个数,为所求的最少的士兵数目。

Sample Input

4
0 1 1
1 2 2 3
2 0
3 0

Sample Output

1

解析

在树形图求解最优化问题,很明显就是一道树形DP的模板题。
我们根据树形DP通常设置状态的套路来设计这道题的状态:f[i][0/1]代表以i为根的子树中的最小士兵数,1代表节点i放了士兵,0代表节点i没放士兵。
对于状态的转移,我们可以分两种情况讨论:

1.节点i放一个士兵,节点i的子节点可以放士兵,也可以不放士兵
2.节点i不放士兵,节点i的各个子节点都必须放士兵

那么所对应的状态转移方程就是:

\[
1.f[i][1]=\sum_{j \in son(i)}min\{f[j][0],f[j][1]\}+1
\\2.f[i][0]=\sum_{j \in son(i)}f[j][1]
\]
注意到题中没有明确的树根的指明,所以我们只要随便找一个入度为0的点当做树根执行记忆化搜索即可。

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
const int N=1500+30;
int n,f[N][2],ans=0x3f3f3f3f,vis[N];vector < int >Link[N];
inline void input(void)
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int index,num,temp;
        scanf("%d%d",&index,&num);
        for(int j=1;j<=num;j++)
        {
            scanf("%d",&temp);
            Link[index].push_back(temp);
            vis[temp]=1;
        }
    }
}
inline void dp(int root)
{
    f[root][1]=1;
    f[root][0]=0;
    for(int i=0;i<Link[root].size();i++)
    {
        int Son=Link[root][i];
        dp(Son);
        f[root][0]+=f[Son][1];
        f[root][1]+=min(f[Son][0],f[Son][1]);
    }
    return;
}
int main(void)
{
    freopen("strategi.in","r",stdin);
    freopen("strategi.out","w",stdout);
    input();
    int root;
    for(int i=0;i<n;i++)
    {
        if(!vis[i])
        {
            root=i;
            break;
        }
    }
    dp(root);
    printf("%d\n",min(f[root][0],f[root][1]));
}

最大利润(SMOJ1782)

Description

政府邀请了你在火车站开饭店,但不允许同时在两个相连接的火车站开。
任意两个火车站有且只有一条路径,每个火车站最多有50个和它相连接的火车站。
告诉你每个火车站的利润,问你可以获得的最大利润为多少。

Input Format

第一行输入整数N(<=100000),表示有N个火车站,分别用1,2,...,N来编号。

接下来N行,每行一个整数表示每个站点的利润,接下来N-1行描述火车站网络,每行两个整数,表示相连接的两个站点。

Output Format

输出一个整数表示可以获得的最大利润

Sample Input

6
10
20
25
40
30
30
4 5
1 3
3 4
2 3
6 4

Sample Output

90

解析

这道题和上一道题很像,都是很明显的树形DP。但是两道题有不同之处:上一题是覆盖相邻的边,但这题是覆盖相邻的点。上一题是必须全部覆盖,这一题是可以不全部覆盖,但不能重叠。我们仍然可以设f[i][0/1]代表以i为根的子树中的最大利润,0代表节点i没有开餐馆,1代表节点i开了餐馆。
状态的转移就和上一题很相似了:

1.若节点i开了餐馆,则它的子节点可以开餐馆,也可以不开
2.若节点i没开餐馆,则它的子节点都不能开餐馆

状态转移方程:
\[
f[i][0]=\sum_{j \in son(i)}max{f[j][1],f[j][0]}
\\f[i][0]=\sum_{j \in son(i)}f[j][0]+a[i]
\]
注意在记忆化搜索的时候需要开一个访问数组标记,避免递归死循环。

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
const int N=100000+80;
int n,f[N][2],a[N],vis[N],ans=0;
vector < int > Link[N];
inline void input(void)
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        Link[u].push_back(v);
        Link[v].push_back(u);
    }
}
inline void dp(int root)
{
    f[root][0]=0;
    f[root][1]=a[root];
    for(int i=0;i<Link[root].size();i++)
    {
        int Son=Link[root][i];
        if(vis[Son])continue;
        vis[Son]=1;
        dp(Son);
        f[root][0]+=max(f[Son][1],f[Son][0]);
        f[root][1]+=f[Son][0];
    }
    return;
}
int main(void)
{
    freopen("profit.in","r",stdin);
    freopen("profit.out","w",stdout);
    input();
    memset(f,0x00,sizeof f);
    memset(vis,0x00,sizeof vis);
    vis[1]=1;
    dp(1);
    printf("%d\n",max(f[1][1],f[1][0]));
}



『战略游戏 最大利润 树形DP』

原文地址:https://www.cnblogs.com/Parsnip/p/10331639.html

时间: 2024-10-08 08:54:07

『战略游戏 最大利润 树形DP』的相关文章

『土地征用 Land Acquisition 斜率优化DP』

斜率优化DP的综合运用,对斜率优化的新理解. 详细介绍见『玩具装箱TOY 斜率优化DP』 土地征用 Land Acquisition(USACO08MAR) Description Farmer John is considering buying more land for the farm and has his eye on N (1 <= N <= 50,000) additional rectangular plots, each with integer dimensions (1

『最短Hamilton路径 状态压缩DP』

状压DP入门 最短Hamilton路径 Description 给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径. Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次. Input Format 第一行一个整数n. 接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(一个不超过10^7的正整数,记为a[i,j]). 对于任意的x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y

P2016 战略游戏——树形DP大水题

P2016 战略游戏 树形DP 入门题吧(现在怎么是蓝色标签搞不懂): 注意是看见每一条边而不是每一个点(因为这里错了好几次): #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=3010; int pre[maxn],last[maxn],other[maxn],l; void add(int x,int y) { l++; pre[l]

『字符合并 区间dp 状压dp』

字符合并 Description 有一个长度为 n 的 01 串,你可以每次将相邻的 k 个字符合并,得到一个新的字符并获得一定分数.得到的新字符和分数由这 k 个字符确定.你需要求出你能获得的最大分数. Input Format 第一行两个整数n,k.接下来一行长度为n的01串,表示初始串. 接下来2^k行,每行一个字符ci和一个整数wi,ci表示长度为k的01串连成二进制后按从小到大顺序得到的第i种合并方案得到的新字符,wi表示对应的第i种方案对应获得的分数. 1<=n<=300,0<

『Exclusive Access 2 dilworth定理 状压dp』

Exclusive Access 2 Description 给出 N 个点M 条边的无向图,定向得到有向无环图,使得最长路最短. N ≤ 15, M ≤ 100 Input Format 第一行一个数M (1≤M≤100). 接下来M行,每行两个大写字母(L 到 Z),最多出线15个不同的大写字母.每行的两个大写字母不会相同. Output Format 第一行输出最长路最短的数值-1. Sample Input 3 P Q Q R R P Sample Output 1 解析 二分答案?想多

树形dp 结论题 bzoj5024 [Jsoi2010]游戏

http://www.lydsy.com/JudgeOnline/problem.php?id=5024 首先吐槽一下题面是有错误的 那一个"或" 应该改成"," 这道题目条件是非常绕的 先看一个简化的问题 我们对于\(u\), \(v\) 如果\(u-v\)是良的 那么染成白色 否则染成黑色 问题转化成一个经典问题 给定一个完全图 求同色三角形的个数 做法就是考虑非同色三角形的个数 找角的个数\(jiao\) 答案就是\(C_n ^ 3 - jiao / 2\)

4.9 省选模拟赛 圆圈游戏 树形dp set优化建图

由于圆不存在相交的关系 所以包容关系形成了树的形态 其实是一个森林 不过加一个0点 就变成了树. 考虑对于每个圆都求出最近的包容它的点 即他的父亲.然后树形dp即可.暴力建图n^2. const int MAXN=100010; int n,m,len; struct wy { ll x,y,r,w; inline int friend operator <(wy a,wy b){return a.r<b.r;} }t[MAXN]; int f[MAXN]; int lin[MAXN],ver

hdu 1561The more, The Better(树形dp&amp;01背包)

The more, The Better Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4949    Accepted Submission(s): 2918 Problem Description ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝

hdu-1561 The more, The Better (树形dp入门,有依赖的背包问题

The more, The Better Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4954    Accepted Submission(s): 2922 Problem Description ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝