动态规划学习 1(最长上升子序列问题)

重新学习dp的第一步,计划学习dp用时40个学时,砥砺前行吧大伙

 

复制代码
//最长上升子序列问题 #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int f[N],a[N],n,res; int main() { cin>>n; for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++) { f[i]=1; for(int j=0;j<i;j++) if(a[i]>a[j]) f[i]=max(f[i],f[j]+1); } for(int i=0;i<n;i++) res=max(res,f[i]); cout<<res; return 0; } // 最长上升子序列优化 // 基于贪心做法 // 首先可以证明整个f数组是单调递增的,即对于任意的索引i < j,都有 f[i] < f[j]。反证法如下: // 若f[i] == f[j],对于f[j]对应的长度为j的最长上升子序列,其倒数第2个元素值一定小于f[j],那么这个倒数第2个元素值可以作为f[i],则有f[i] < f[j]。 // 同理可证f[i] > f[j]的情形。 // 继续分析: // 如已扫描序列1 4 2 3,根据我们的定义,有f[3] = 3(序列1 2 3的尾元素),再往后扫描时,如果是4,即序列为1 4 2 3 4时,就可以根据f[3] = 3(3 < 4)往后延长一个LIS长度(即f长度变为4),而且使得f[4] = 4(即1 2 3 4的尾元素)。这样显然是合理的,而且可以为以后LIS长度的更新以及尾元素的更新做铺垫。 // 或者从反面考虑:如果我们f[i]存储的不是长度为i的最长上升子序列的尾元素,如f[3] = 5(存1 4 5的尾元素),那么扫描后续元素就无法最大化地延长上升序列,无法达到题目要求。 // 注意在思考问题时,从集合的角度考虑问题,即每个f[i]首先是关于所有长度为i的上升序列的函数,其次,f[i]存的是这些上升序列尾元素的最小值。 // 那么在扫描原数组的时候,对于第i个数,可以根据前i-1个数得到的f数组的结果,对f数组进行长度或内容的更新,具体表现如下:(设f数组的实际长度为cnt) // 1、若a[i] > f[cnt],则对于从1至cnt的f中的每一个f[j],都有a[i] > f[j](因为前面已经证明f单调递增)。那么当前的a[i]可以作为长度为cnt+1的上升子序列的最小元素(因为当前只有这么一个长度为cnt+1的上升序列),即f[cnt+1] = a[i],同时cnt++. // 2、若a[i] <= f[cnt],则无法使得上升序列变长,因为f[cnt]是长度为cnt的所有上升序列尾元素的最小值。因此,它的作用是,更新对应的一个f。显然,对于从后往前第一个小于a[i]的f[j]而言,a[i]可以接到f[j]所代表的那个最优上升序列后面,从而更新f[j+1] // (长度为j+1的上升序列的尾元素最小值)。因为从刚才的叙述可以知道,原f[j+1] >= a[i]。故这样a[i]的作用是直接改变f的一个恰当元素值,同时为最终cnt的更新作准备(通过将一个f的元素值变小,使得后面元素接到前面更具有可能)。 #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,m,k,res,f[N],q[N],a[N]; int main() { cin>>n; for(int i=0;i<n;i++) cin>>a[i]; q[0]=-2e9; for(int i=0;i<n;i++) { if(q[res]<a[i]) q[++res]=a[i]; else{ int l=0,r=res; while(l<=r){ int mid=l+r>>1; if(q[mid]>=a[i]) r=mid-1; else l=mid+1; } q[l]=a[i]; } } cout<<res; return 0; }
复制代码
//输出最长上升子序列 #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,m,k,res,f[N],q[N],a[N],pos[N],maxx=2e9,cnt,ans[N]; int main() { cin>>n; for(int i=0;i<n;i++) cin>>a[i]; q[0]=-2e9,pos[0]=1; for(int i=0;i<n;i++) { if(q[res]<a[i]) q[++res]=a[i],pos[i]=res; else{ int l=0,r=res; while(l<=r){ int mid=l+r>>1; if(q[mid]>=a[i]) r=mid-1; else l=mid+1; } q[l]=a[i]; pos[i]=l; } } cnt=res; cout<<"最长上升子序列的长度为:"<<res<<endl; for(int i=n-1;i>=0;i--){ if(res==0) break; if(pos[i]==res) ans[res]=a[i],res--; } cout<<"最长上升子序列为:"; for(int i=1;i<=cnt;i++) cout<<ans[i]<<" "; return 0; }
复制代码

 

//最长上升子序列合 //f[i]是第i位的最大上升子序列合,设k为倒数第二位的话 //那f[i]=f[k]+a[i],以此类推 #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,res,a[N],f[N]; int main() { cin>>n; for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++) { f[i]=a[i]; for(int j=0;j<i;j++) if(a[i]>a[j]) f[i]=max(f[i],f[j]+a[i]); } for(int i=0;i<n;i++) res=max(res,f[i]); cout<<res; return 0; } //最长不上升子序列(下降) #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,a[N],q[N],res; int main() { cin>>n; q[0]=2e9; for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++) { if(q[res]>=a[i]) q[++res]=a[i]; else{ int l=0,r=len; while(l<=r){ int mid=l+r>>1; if (q[mid]<a[i]) r=mid-1; else l=mid+1; } q[l]=a[i]; } } cout<<res; return 0; } //怪盗基德滑翔翼 #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,res,a[N],q[N],len; int main() { cin>>n; q[0]=-2e9; for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++) { if(q[len]<a[i]) q[++len]=a[i]; else{ int l=0,r=n-1; while(l<=r){ int mid=l+r>>1; if(q[mid]>=a[i]) r=mid-1; else l=mid+1; } q[l]=a[i]; } } res=len,len=0; memset(q,0,sizeof q); q[0]=2e9; for(int i=n-1;i>=0;i--) { if(q[len]<a[i]) q[++len]=a[i]; else{ int l=0,r=n-1; while(l<=r){ int mid=l+r>>1; if(q[mid]>=a[i]) r=mid-1; else l=mid+1; } q[l]=a[i]; } } cout<<max(res,len)<<endl; return 0; } //登山,合唱队形:https://www.luogu.com.cn/problem/P1091 #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int f[N],g[N],n,m,res,a[N],t,q[N]; int main() { cin>>n; for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++) { f[i]=1; for(int j=0;j<i;j++) if(a[i]>a[j]) f[i]=max(f[i],f[j]+1); } for(int i=n-1;i>=0;i--) { g[i]=1; for(int j=n-1;j>=i;j--) if(a[i]>a[j]) g[i]=max(g[i],g[j]+1); } for(int i=0;i<n;i++) res=max(res,g[i]+f[i]-1); cout<<res;//登山 cout<<n-res;//合唱队行 return 0; } //友好城市:https://www.luogu.com.cn/problem/P2782 //只需要对y从城市排序,然后求x的最长上升子序列就可以 #include<bits/stdc++.h> using namespace std; const int N=2e5+10; int x,n,res,len=0,q[N],f[N]; struct node { int x,y; bool operator<(const node&w)const { return y<w.y; } }city[N]; int main() { cin>>n; for(int i=0;i<n;i++) cin>>city[i].x>>city[i].y; sort(city,city+n); q[0]=-2e9; for(int i=0;i<n;i++) { if(q[len]<city[i].x) q[++len]=city[i].x; else{ int l=0,r=len; while(l<=r){ int mid=l+r>>1; if(q[mid]>=city[i].x) r=mid-1; else l=mid+1; } q[l]=city[i].x; } } cout<<len; return 0; } #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,m,a[N],q[N],res; int main() { while(cin>>a[n]) n++; q[0]=2e9; for(int i=0;i<n;i++) { if(q[res]>=a[i]) q[++res]=a[i]; else{ int l=0,r=res; while(l<=r){ int mid=l+r>>1; if(q[mid]<a[i]) r=mid-1; else l=mid+1; } q[l]=a[i]; } } cout<<res<<endl; memset(q,0,sizeof q); q[0]=-2e9,res=0; for(int i=0;i<n;i++) { if(q[res]<a[i]) q[++res]=a[i]; else{ int l=0,r=res; while(l<=r){ int mid=l+r>>1; if(q[mid]>=a[i]) r=mid-1; else l=mid+1; } q[l]=a[i]; } } cout<<res<<endl; return 0; } #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,a[N],res,f[N],ans; bool vis[N]; void dfs1(int num) { if(num==n){ res++; return; } if(vis[num]) dfs1(num+1); for(int i=num+1;i<n;i++) if(a[i]>a[num]) vis[i]=true,dfs1(i); } void dfs2(int num) { if(num==n){ ans++ return; } if(vis[num]) dfs2(num+1); for(int i=num+1;i<n;i++) if(a[i]<a[num]) vis[i]=true,dfs2(i); } int main() { while(cin>>n&&n!=0) { res=0; memset(vis,false,sizeof vis); for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++) if(!vis[i]) dfs1(i); memset(vis,false,sizeof vis); for(int i=0;i<n;i++) if(!vis[i]) dfs2(i); res=min(res,ans); cout<<res<<endl; } return 0; } //导弹防御系统:https://www.acwing.com/problem/content/description/189/\ #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int a[N],q[N],q1[N],res,n; void dfs(int now,int up,int down)//当前的数,上升序列的个数,下降序列的个数 { if(up+down>=res) return;//剪枝 if(now==n){ res=up+down; return; } //情况一,上升子序列; //终于明白q的意义了,q里面代表的不是一个上升子序列,而是某个上升子序列的最大值 //然后利用当前数的大小与某个上升子序列的最大值比,如果大,那就接上去,如果小,那就比下一个,如果都不行,那就新建一个 int len=0; while(len<up&&a[now]<=q[len]) len++;//q序列是单调递增的,模拟一下发现,如果当前这个数无法加入当前这个上升子序列 //也就是说,我们的q头是上升子序列的最大值,如果当前的数比它小,那么它就无法加入,而len就是新的子序列的第一个元素位置 //这里的while循环可以用二分代替,也就是咱们那个nlogn的时间复杂度算法,所以我们这里用二分! //就是来找第一个<=q的元素!! int t=q[len];//为了回溯做准备 q[len]=a[now];//如果能接,那就更新序列的最大值,如果不能,那就这个就是在新的序列中更新最大值 if(len<up) dfs(now+1,up,down);//如果len>=up,说明什么? //说明len的数都要比当前的数大,那就要开一个新得序列; //如果len<up,说明当前这个数可以加到某一个序列中去,并且更新这个序列的最大值,就不用新建序列了!!!! else dfs(now+1,up+1,down);//如果=它,说明这个序列需要更新了,要+1; q[len]=t; //情况二,下降子序列; len=0; while(len<down&&a[now]>=q1[len]) len++;//与上升子序列顺序相反,思路差不多; t=q1[len]; q1[len]=a[now]; if(len<down) dfs(now+1,up,down); else dfs(now+1,up,down+1); q1[len]=t; } int main() { while(cin>>n,n){ for(int i=0;i<n;i++) cin>>a[i]; res=n; dfs(0,0,0); cout<<res<<endl; } return 0; } //最长公共上升子序列:http://poj.org/problem?id=2127 //1-1 朴素二维数组做法 #include<bits/stdc++.h> using namespace std; const int N=1010; int a[N],f[N][N],res,b[N],n; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) cin>>b[i]; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { f[i][j]=f[i-1][j]; if(a[i]==b[j]) { f[i][j]=max(f[i][j],1); for(int k=1;k<j;k++) if(b[j]>b[k]) f[i][j]=max(f[i][j],f[i][k]+1); } } for(int i=0;i<n;i++) res=max(res,f[n][i]); cout<<res; return 0; } //1-2 优化算法on2 #include<bits/stdc++.h> using namespace std; const int N=1010; int a[N],f[N][N],res,b[N],n,m; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; cin>>m; for(int i=1;i<=m;i++) cin>>b[i]; for(int i=1;i<=n;i++) { int maxv=1; for(int j=1;j<=m;j++) { f[i][j]=f[i-1][j]; if(a[i]==b[j]) f[i][j]=max(f[i][j],maxv); if(a[i]>b[j]) maxv=max(maxv,f[i][j]+1); } } for(int i=1;i<=n;i++) res=max(res,f[n][i]); cout<<res; return 0; } //1-3 容易理解的优化算法 #include<bits/stdc++.h> using namespace std; const int N=2020; int n,m,res,f[N][N],a[N],b[N]; int g[N][N];//g[n][n]表示的是g数组,在满足a[i]>b[j]的情况下,f[i][j]+1的最大值 int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) cin>>b[i]; for(int i=1;i<=n;i++) { g[i][0]=1;//从i开始,b数组的第0位设为1 for(int j=1;j<=n;j++)//枚举b数组 { f[i][j]=f[i-1][j]; if(a[i]==b[j]) f[i][j]=max(f[i][j],g[i][j-1]); g[i][j]=g[i][j-1];//目前肯定是上一位的最大值嘛 if(a[i]>b[j]) g[i][j]=max(g[i][j],f[i-1][j]+1);//如果符合,更新 } } for(int i=1;i<=n;i++) res=max(res,f[n][i]); cout<<res; return 0; }
复制代码

 


__EOF__

本文作者Sakurajimamai
本文链接https://www.cnblogs.com/o-Sakurajimamai-o/p/17464369.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   o-Sakurajimamai-o  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
-- --
点击右上角即可分享
微信分享提示