动态规划,dp
来源:互联网 发布:mindmap for mac 编辑:程序博客网 时间:2024/06/05 07:39
动规分类
- 线性动规
- 区间动规
- 树形动规
区间动规
根据题目要求,全局最优满足局部最优;
典型题例
加分二叉树(洛谷1040)
题目介绍
题目描述
设一个n 个节点的二叉树T 的中序遍历为(1,2,3,…,n),其中数字 1,2,3,…,n 为节点编号。
每个节点都有一个分数(均为正整数),记第j 个节点的分数为dj。二叉树T 及它的每个子树都有 一个加分,任意一棵子树S(包括T 本身)的加分等于S 的左子树的加分×S 的右子树的加分+S的根的分数。 若某棵子树为空,规定其加分为1。叶子的加分就是叶节点本身的分数,不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树T。要求输出T 的最高加分和前序遍历。
输入格式
第1 行:一个整数n(n<30),为节点个数。
地2 行:n个用空格隔开的整数,为每个节点的分数(分数<100)。
输出格式
第1 行:一个整数,为最高加分(结果不会超过4,000,000,000)。
第2 行:n个用空格隔开的整数,为该树的前序遍历。
输入样例
5
5 7 1 2 10
输出样例
145
3 1 2 4 5
题目分析
由于是中序遍历,因此永远满足左子树节点编号<根节点编号<右子树节点编号,虽是树形,但不存在选择与决策问题,因此不是树规;
在全局最优的同时,区间最优同时满足,因此选用区间dp;
动态转移方程
f[r,l]=max{f[i,k] * f[k,j]+a[i][i]}
#include<cstdio>#include<iostream>using namespace std;int a[20][20],r[20][20];//r数组存储区间根节点int n;void find(int x,int y)//前序遍历,dfs顺序 { if(x<=y) { printf("%d",&r[x][y]); find(x,r[x][y]-1); find(r[x][y]+1,y); } return;}int main()//区间dp,局部最优满足全局最优,当前决策具有后效性 { scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]=1; for(int i=1;i<=n;i++) { scanf("%d",&a[i][i]); r[i][i]=i; } for(int i=n;i>=1;i--)//a[i][j]枚举的是从i到J的顶点; for(int j=1+i;j<=n;j++)//枚举区间首,尾 for(int k=i;k<=j;k++) { if(a[i][j]<a[i][k-1]*a[k+1][j]+a[k][k])//同理,枚举区间i~k-1,k+1~j的最大值,再加上顶点自身值 { a[i][j]=a[i][k-1]*a[k+1][j]+a[k][k]; r[i][j]=k;//将k定为当前区间根节点 } } printf("%d",a[1][n]);//输出的是从1~n的区间最大值; }
线性动规
典型题例
导弹拦截,合唱队形;
以下是最长上升子序列和最长下降子序列模板
#include<cstdio>#include<iostream>using namespace std;int a[10001],b[10001],c[10001];int main(){ int n=1; int maxn=0,mine=0; while(scanf("%d",&a[n])){n++;}n--; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++)//最长上升 { b[i]=1; for(int j=1;j<=i-1;j++) if((a[i]>=a[j])&&(b[j]+1>b[i]))b[i]=b[j]+1; if(b[i]>maxn)maxn=b[i]; } for(int i=n;i>=1;i--)//最长下降 { c[i]=1; for(int j=i+1;j<=n;j++) { if((a[i]<a[j])&&c[j]+1>c[i])c[i]=c[j]+1; } if(mine<c[i])mine=c[i]; } printf("%d%d",maxn,mine); return 0;}
二分优化的最长上升子序列
#include<cstdio>#include<iostream>using namespace std;int n,top,a[100005],t;int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&t); if(t>a[top]) a[++top]=t; else { int low=1,high=top,mid; while(low<=high) { mid=(low+high)/2; if(a[mid]<t) low=mid+1; else high=mid-1; } a[low]=t; }} printf("%d",top);}
树状动规
没有上司的晚会
题目介绍
有个公司要举行一场晚会。
为了能玩得开心,公司领导决定:如果邀请了某个人,那么一定不会邀请他的上司
(上司的上司,上司的上司的上司……都可以邀请)。
每个参加晚会的人都能为晚会增添一些气氛,求一个邀请方案,使气氛值的和最大。
input:
第1行一个整数N(1<=N<=6000)表示公司的人数。
接下来N行每行一个整数。第i行的数表示第i个人的气氛值x(-128<=x<=127)。
接下来每行两个整数L,K。表示第K个人是第L个人的上司。
输入以0 0结束。
output:
一个数,最大的气氛值和。
input:
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
output:
5
思路分析
本题满足树的特征,即有向无环,可以有多个后继,但只能有一个前驱,
最优化问题,当前节点的选择只与其儿子有关,满足无后效性,可以选择树状dp,于是建树,DFS;
#include<iostream>#include<cstdio> using namespace std;int f[6001][2],father[6001];bool v[6001];int n;int find_max(int a,int b){ if (a>b) return a; else return b;}void dfs(int k)//f[i][0]储存不选用当前人的最大值,f[i][1]储存选用的最大值 { v[k]=false; for (int i=1;i<=n;i++) if (v[i] && father[i]==k) { dfs(i); f[k][0]+=f[i][1]; f[k][1]+=find_max(f[i][0],f[i][1]); }}int main(){ scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&f[i][0]); int l,k,root; root=0;bool bk=true; while (scanf("%d%d",&l,&k),l+k>0) { father[l]=k; if (root==l || bk==true)//由于输入的无序性,因此必须找出根节点 { root=k;bk=false;//根节点等于当前人的上司 } } memset(v,true,sizeof(v)); dfs(root);//从根节点开始搜索 printf("%d\n",find_max(f[root][0],f[root][1]));//输出决策是否选用第一个人(第一个人的数组已储存选与不选的最大值) return 0;}
- 动态规划DP
- 动态规划(DP)
- Hilary动态规划DP
- POJ2192Zipper动态规划DP
- DP动态规划
- DP专辑 动态规划
- DP---动态规划
- 动态规划DP原理
- 动态规划(DP)
- SGU116 动态规划 DP
- DP 动态规划
- HDU1203-动态规划DP
- 动态规划-dp
- DP(动态规划) PPT
- 动态规划DP
- 【dp动态规划总结】
- 动态规划(DP)算法
- 动态规划DP
- 有向边框RotatedRect的绘制 以及外边框计算
- 动态顺序表----C语言实现
- 设计模式——策略模式(Strategy Pattern)
- java安全架构____数字证书结构和类别
- PAT A1102. Invert a Binary Tree
- 动态规划,dp
- Java多线程之并发协作生产者消费者设计模式
- python装饰器学习笔记
- 设备像素密度测试 (-webkit-min-device-pixel-ratio)
- How to get price from apple store or google store
- js 浏览器对象模型 (BOM)
- sql查询当天、本周、本月记录
- LeetCode
- HDU1256 画8