最长上升子序列
原题题面我就不过多赘述了;
为什么说要那样子去列我们的状态表示和状态转移方程呢;
首先因为最后一个数不一定是最大的因此不一定是他结尾的;
而且呢因为我们要转移的话,必然是需要对已知情况进行判断的,如果说现在遍历到的那个数大于之前的某个数的时候才会更新一遍状态,所以我们要保留的是,当前结尾的那个数字;
这题仔细品一品就能品出来最优子结构并且带有相关信息,并且能转移的性质;
代码
acwing题解里面来的,懒得写了
#include <iostream>
using namespace std;
const int N = 1010;
int n;
int w[N], f[N];
int main() {
cin >> n;
for (int i = 0; i < n; i++) cin >> w[i];
int mx = 1;
for (int i = 0; i < n; i++) {
f[i] = 1;
for (int j = 0; j < i; j++) {
if (w[i] > w[j]) f[i] = max(f[i], f[j] + 1);
}
mx = max(mx, f[i]);
}
cout << mx << endl;
return 0;
}
最长公共子序列
太懒了;
这篇题解讲的很明白;
最长公共子序列
还是要注意一下最优子结构的性质;
代码
#include <iostream>
using namespace std;
const int N = 1010;
int n, m;
char a[N], b[N];
int f[N][N];
int main() {
cin >> n >> m >> a + 1 >> b + 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i] == b[j]) {
f[i][j] = f[i - 1][j - 1] + 1;
} else {
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
}
}
cout << f[n][m] << '\n';
return 0;
}
例题
有一些诸如怪盗基德的滑翔翼登山合唱队形这样的牛马题目就不讲了;
最大上升子序列和
这个题目有点意思的,我们所需要的信息依然是每个子序列的结尾,那么我们所需要的状态标识还是不变的,那么变得就是转移方程了;
之前是原序列长度加1,现在改成加上那个数值就行了没啥好说的;
拦截导弹
最长不上升子序列问题,在选取屌弹拦截系统的时候应该用贪心选择性来进行选择;
显然吧。。。。
其实是可以证明的,我们可以用调整法,我确信我已经找到了一个美妙的证明,可惜这里空白太小写不下;
导弹防御系统
这题已经和DP没什么关系了,主要讲的是回溯的搜索;
回溯不回溯的事情呢还是要看搜索时候的具体情况;
#include<iostream>
#include<cstdio>
using namespace std;
const int N=55;
int n,h[N],up[N],down[N],ass;
void dfs(int u,int su,int sd){
if(su+sd>=ass)return;
if(u==n){
ass=min(ass,su+sd);
return;
}
int k=0;
while(k<su&&up[k]>=h[u])k++;
if(k<su){
int t=up[k];
up[k]=h[u];
dfs(u+1,su,sd);
up[k]=t;
}else{
up[k]=h[u];
dfs(u+1,su+1,sd);
}
k=0;
while(k<sd &&down[k]<=h[u])k++;
if(k<sd){
int t=down[k];
down[k]=h[u];
dfs(u+1,su,sd);
down[k]=t;
}else{
down[k] = h[u];
dfs(u+1,su,sd+1);
}
}
int main()
{
while(cin>>n,n){
for(int i=0;i<n;i++)scanf("%d",&h[i]);
ass=n;
dfs(0,0,0);
printf("%d\n",ass);
}
return 0;
}
最长公共上升子序列
最后一个例题。
内容讲解可以去看Y总视频,这里来个代码,贼几把妙的那种;
原题解链接LCIS
#include<iostream>
#include<cstdio>
using namespace std;
const int N=3050;
int n,a[N],b[N],f[N],maxx;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
for(int i=1;i<=n;i++){
maxx=0;
for(int j=1;j<=n;j++){
if(b[j]<a[i]&&maxx<f[j])maxx=f[j];
if(b[j]==a[i])f[j]=maxx+1;
}
}
maxx=0;
for(int i=1;i<=n;i++)if(maxx<f[i])maxx= f[i];
printf("%d",maxx);
return 0;
}
今天发现了最长上升子序列的数据加强版,卡了原本N^2的做法,加了个log,分享下新做法;
下面这个代码是以友好城市这道题为例子的;
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2e5+10;
struct Node{
int x, y;
}a[N];
int n,d[N],len;
bool cmp(Node X,Node Y){
return X.x<Y.x;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
}
sort(a+1,a+1+n,cmp);
d[++len]=a[1].y;
for(int i=2;i<=n;i++){
int tmp=upper_bound(d+1,d+len+1,a[i].y)-d;
d[tmp]=a[i].y;
if(tmp>len){
len++;
}
}
printf("%d",len);
return 0;
}