首页 > 其他分享 >线性dp

线性dp

时间:2024-02-17 16:33:40浏览次数:27  
标签:反链 int 样例 导弹 线性 FJ 奶牛 dp

基本应用:
最长上升子序列:
题目描述
设有由n个不相同的整数组成的数列,记为:b(1)、b(2)、……、b(n)且b(i)<>b(j) (i<>j),若存在i1<i2<i3< … < ie 且有b(i1)<b(i2)< … <b(ie)则称为长度为e的不下降序列。程

序要求,当原数列出之后,求出最长的上升序列。

例如13,7,9,16,38,24,37,18,44,19,21,22,63,15。例中13,16,18,19,21,22,63就是一个长度为7的不下降序列,同时也有7 ,9,16,18,

19,21,22,63长度为8的不下降序列。
输入格式
只有一行,为若干正整数(最多1000个数)
输出格式
为两行,第一行为最上升序列的长度。 第二行为该序列
样例
样例输入
13 7 9 16 38 24 37 18 44 19 21 22 63 15
样例输出
max=8
7 9 16 18 19 21 22 63

解:板子题,不多解释

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,m,f[1111],a[1111],ans,way[1111],te;
//递归输出路径
void p(int x){
	if(x==0)return;
	p(way[x]);
	cout<<a[x]<<" ";
}
int main(){
	while(cin>>n)a[++m]=n;
	for(int i=1;i<=m;i++){//遍历每个数
		f[i]=1;//最少上升子序列数也是1
		for(int j=1;j<i;j++){//看当前数前面的最长上升子序列长度f[j],如果这个数大于其最大数,则更新此数为f[j]+1
			if(a[i]>a[j]&&f[i]<f[j]+1){
				f[i]=f[j]+1;
				way[i]=j;//记录路径
			}
		}
		if(ans<f[i]){
			ans=f[i];//更新ans
			te=i;//并找到路径的尾
		}
	}
	cout<<"max="<<ans<<endl;
	p(te);
	return 0;
}

扩展应用:
拦截导弹简单版:
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,导弹数不超过1000),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式
输入只有一行,为若干个正整数,一次为导弹的高度。
输出格式
第一行为最多能拦截的导弹数;
第二行为要拦截所有导弹最少要配备的系统数
样例
样例输入
389 207 155 300 299 170 158 65
样例输出
6
2

解:这道题虽说是板子,但他让求了系统数,所以—————看解释:
定理
在X中,对于元素a,如果任意元素b,都有a≤b,则称a为极小元。
定理1:令(X,≤)是一个有限偏序集,并令r是其最大链的大小。则X
可以被划分成r个但不能再少的反链。
其对偶定理称为Dilworth定理:
令(X,≤)是一个有限偏序集,并令m是反链的最大的大小。则X可以
被划分成m个但不能再少的链。
虽然这两个定理内容相似,但第一个定理证明要简单一些。此处就只
证明定理1。

证明:设p为最少反链个数
(1)先证明集合X不能划分成小于r个反链。由于r是最大链
C的大小,C中任两个元素都可比,因此C中任两个元素都
不能属于同一反链。所以p>=r(p大于等于r)。
(2)设集合X1=集合X,A1是X1中的极小元的集合。从X1中
删除A1得到X2。注意到对于X2中任意元素a2,必存在X1中
的元素a1,使得a1<=a2。令A2是X2中极小元的集合,从X2
中删除A2得到X3……,最终会有一个Xk非空而Xk+1为空。
于是A1,A2,…,Ak就是X的反链的划分,同时存在链
a1<=a2<=…<=ak,其中ai在Ai内。由于r是最长链大小,
因此r>=k。由于X被划分成了k个反链,因此r>=k>=p。
(3)因此r=p,定理1得证。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,m,f[1111],a[1111],ans,way[1111],te,vis[1111];
int main(){
	while(cin>>n)a[++m]=n;
    //先正序求最大不下降子序列,即为最多能拦截的导弹数
	for(int i=1;i<=m;i++){
		f[i]=1;
		for(int j=1;j<i;j++){
			if(a[i]<=a[j]&&f[i]<f[j]+1){
				f[i]=f[j]+1;
			}
		}
		if(ans<f[i]){
			ans=f[i]; 
		}
	}cout<<ans<<endl;
	ans=0;
    //求最长下降子序列,由证明得即为最少要配备的系统数
	for(int i=1;i<=m;i++){
		f[i]=1;
		for(int j=1;j<i;j++){
			if(a[i]>a[j]&&f[i]<f[j]+1){
				f[i]=f[j]+1;
			}
		}
		if(ans<f[i]){
			ans=f[i]; 
		}
	}cout<<ans;
	return 0;
}

变形:
麻烦的聚餐
题目描述
为了避免餐厅过分拥挤,FJ要求奶牛们分3批就餐。
每天晚饭前,奶牛们都会在餐厅前排队入内,按FJ的设想,所有第3批就餐的奶牛排在队尾,队伍的前端由设定为第1批就餐的奶牛占据,中间的位置就归第2批就餐的奶牛了。
由于奶牛们不理解FJ的安排,晚饭前的排队成了一个大麻烦。
第i头奶牛有一张标明她用餐批次D_i(1 <= D_i <= 3)的卡片。虽然所有N(1 <= N <= 30,000)头奶牛排成了很整齐的队伍,但谁都看得出来,卡片上的号码是完全杂乱无章的。
在若干次混乱的重新排队后,FJ找到了一种简单些的方法:奶牛们不动,他沿着队伍从头到尾走一遍,把那些他认为排错队的奶牛卡片上的编号改掉,最终得到一个他想要的每个组中的奶牛都站在一起的队列,例如111222333或者333222111。
哦,你也发现了,FJ不反对一条前后颠倒的队 列,那样他可以让所有奶牛向后转,然后按正常顺序进入餐厅。
你也晓得,FJ是个很懒的人。他想知道,如果他想达到目的,那么他最少得改多少头奶牛卡片上的编号。所有奶牛在FJ改卡片编号的时候,都不会挪位置

输入格式
第1行: 1个整数:N
第2..N+1行: 第i+1行是1个整数,为第i头奶牛的用餐批次D_i
输出格式
第1行: 输出1个整数,为FJ最少要改几头奶牛卡片上的编号,才能让编号变成他设想中的样子

样例
样例输入
5
1
3
2
1
1
样例输出
1
数据范围与提示
【输入说明】
队列中共有5头奶牛,第1头以及最后2头奶牛被设定为第一批用餐,第2头奶牛的预设是第三批用餐,第3头则为第二批用餐。
【输出说明】
如果FJ想把当前队列改成一个不下降序列,他至少要改2头奶牛的编号,一种可行的方案是:把队伍中2头编号不是1的奶牛的编号都改成1。不过,如果FJ选择把第1头奶牛的编号改成3就能把奶牛们的队伍改造成一个合法的不上升序列了。

解:一眼看去,这不就是线性dp的板子题吗,要是这样认为,写出代码后,你会被现实伤的体无完肤,又一眼看去,发现数据范围是n<=30000,这就会让我们n^2的复杂度很难办,限时1s,接近1e9的运算量,直接就是kuku超时。那怎么办呢,观察一下这道题的特点,它的数只有1,2,3。那就好办了。直接换一种思路,把第二层循环直接换成三个数的遍历,推一个新的状态转移方程就行了。如下:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int f[31111],w[31111],m,n,k[31111][4],l[31111][4],ans;//三个数,k,l第二维开4就够用了
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%d",&w[i]);
		f[n-i+1]=w[i];//倒序存一遍,一会和正序一起算就行
	}
	for(int i=1;i<=n;i++){
		k[i][1]=k[i-1][1]+(f[i]!=1);//要想把第i个置成1,就需要i前面的全是1,如果自己是1,就不需要变了,否则把自己变成1并把上一个状态+1;
		k[i][2]=min(k[i-1][2],k[i-1][1])+(f[i]!=2);//要想把第i个置成2,就需要i前面的不是1就是2,取一个最小值即可,如果自己是2,就不需要变了,否则把自己变成2并把上一个状态+1;
		k[i][3]=min(min(k[i-1][2],k[i-1][1]),k[i-1][3])+(f[i]!=3);//要想把第i个置成3,i前就没有限制了,取一个最小值即可,如果自己是3,就不需要变了,否则把自己变成3并把上一个状态+1;
        //再倒序遍历一遍即可,和上面一样,一会取一下两种的最小即可
		l[i][1]=l[i-1][1]+(w[i]!=1);
		l[i][2]=min(l[i-1][2],l[i-1][1])+(w[i]!=2);
		l[i][3]=min(min(l[i-1][2],l[i-1][1]),l[i-1][3])+(w[i]!=3);
	} 
	ans=1111111;//要求最小,先把ans赋成一个极大值
    //所有情况遍历一遍取最小
	for(int i=1;i<=3;i++){
		ans=min(ans,min(k[n][i],l[n][i]));
	}
	cout<<ans;
	return 0;
} 

标签:反链,int,样例,导弹,线性,FJ,奶牛,dp
From: https://www.cnblogs.com/houburzyx/p/18018101

相关文章

  • dp总结(背包,线性,区间,坐标,树形)
    背包dp0/1背包这种背包会提供可选的物品,背包的容积以及每件物品的价值,并且在选择物品是每件物品只有选一件或不选两种状态。例题输入4512243445输出8二维解法代码//状态转移方程为:f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i])#include"bits/stdc++.h"using......
  • 坐标dp
    坐标dp典型例题:传纸条题目描述小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传......
  • 线性DP
    这篇主要涉及线性DP。先介绍给模型,求最长上升子序列。点击查看代码#include<bits/stdc++.h>usingnamespacestd;constintN=1020;intn;intf[N],ans,a[N];intpre[N],te;voidoutput(intx){ if(x==0) { return; } output(pre[x]); cout<<a[x]<<"";......
  • 区间dp
    区间dp区间dp属于线性dp的一种,以“区间长度”作为dp的“阶段”,用两个坐标(区间的左、右端点)描述每个维度。区间dp中,一个状态往往由若干个比它更小且包含于它的区间所代表的阶段转移而来,所以区间dp的决策往往就是划分dp的方法。典型例题:石子合并for(inti=1;i<=n;i++)f[i][i]=......
  • 五大基础dp
    动规条件•最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。•无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。•有重叠子问题:即子问题之......
  • 背包dp
    01背包:所谓01背包,就是每种物体有一个价值且数量只有一个,给定一个背包容量,在不超出背包容量的前提下在n个物体中取m个,求最大价值。点击查看代码//f[i]指体积为i时的最大价值for(inti=1;i<=n;i++){//第一层循环是指遍历每种物体,n是物体种数 for(intj=m;j>=v[i];j--){//第......
  • 背包DP
    下面介绍一下背包DP主要题型基础模型(没什么好说的,上模板)1.01背包最主要的模板,应用很多,很重要!!!倒着遍历容积,不会受后选小的f[i][j]影响,已经选过的物品不会再选一遍。点击查看代码#include<bits/stdc++.h>usingnamespacestd;constintN=10020;intf[N];intn,m;intv......
  • 关于动态规划(Dynamic Programming)的若干思考 ------ [1.背包dp]
    背包dp;1.01背包(1)领域展开#include<bits/stdc++.h>//simpleusingnamespacestd;constintmaxm=2024;intn,m;intw[maxm],v[maxm],f[maxm][maxm];intmain(){ cin>>n>>m; for(inti=1;i<=n;i++) cin>>v[i]>>w[i]; for(i......
  • 背包dp
    1、01背包f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i])二维为背包现有容量,一维为前i个物品表示在前i个物品所能选取的最大价值在判断第i个的最大值时要由前一个的状态转移过来;即下一层的状态由上一层转移来;可以直接省掉第一维(压维),从后往前更新过来,若还是正序就会出现一种情况......
  • dp的优化(单队,斜率)
    1.单调队列优化\(dp\)维护最小值:\(x\leqslantq.tail()\)维护最大值:\(x\geqslantq.tail()\)其实原理不难,当\(dp\)的转移源头是一个区间时,往往使用单调队列来维护区间最值(一般队列里装下标以方便维护区间大小,但也只是一般情况),节省了处理区间的时间(甚至噶掉一维),重点是对区间的......