线性DP

线性DP

LIS最长上升子序列

——Longest increasing subsequence

转自blog

暴力:

为了上升我们肯定要知道我们当前阶段最后一个元素为多少,为了最长我们还要知道当前我们的序列有多长
(F[i]) 表示以 (A[i]) 为结尾的最长上升子序列的长度,
为了保证保证元素单调递增我们肯定只能从$ i$ 前面的且末尾元素比$ A[i]$ 小的状态转移过来

[F[i]=max(f[j]+1)~(0<=j<i,a[j]<a[i]) ]

边转移转更新答案

for(int i=1;i<=n;i++){
	for(int j=1;j<i;j++)
		if(a[j]<a[i])
			f[i]=max(f[i],f[j]+1);
	ans=max(ans,f[i]);
}	

树状数组优化

原博客优化1代码是有锅的

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
const int N =103,INF=0x7f7f7f7f;
struct Node{
    int val,id;
}a[N]; 
int c[N];
int n;
bool cmp(Node a,Node b) {
    return a.val==b.val?a.id<b.id:a.val<b.val;
}
void upd(int x,int y) {
    for(;x<=n;x+=x&(-x)) c[x]=max(c[x],y);
}
int query(int x) {
    int res=0;
    for(;x;x-=x&(-x)) res=max(res,c[x]);
    return res;
}
int main() {
    int ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i].val);
        a[i].id=i;//有时要离散化 
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++) {
        int maxx=query(a[i].id);//查询编号小于等于id[i]的LIS最大长度
        upd(a[i].id,++maxx);//把长度+1,再去更新前面的LIS长度
        ans=max(ans,maxx);//更新答案
    }
    printf("%d
",ans);
    return 0;
}

贪心+二分查找优化

不上升g/下降f子序列

    g[1]=f[1]=a[1];
    for(int i=2;i<=n;i++){
        if(g[now]>=a[i]) g[++now]=a[i];
        else g[upper_bound(g+1,g+now+1,a[i],cmp())-g]=a[i];
        if(f[con]<=a[i])f[++con]=a[i];
        else f[upper_bound(f+1,f+con+1,a[i])-f]=a[i];
    }
ans=now or con

LCS最长公共子序列

——longest common subsequence

公共子序列和公共子串的区别

公共子串是指连续的公有字符串,公共子序列是指两个字符串都任意删除0个或多个字符后得到的公有字符串,子序列可以是不连续的。

举个例子吧,有两个字符串,串A为“1 2 3 4 5 6 7”,串B 为“1 3 4 5 8 7”,很明显,A和B的公共子串为“3 4 5”,A和B的公共子序列可以是 “3 5”,或是“1 3 7”,等等。

最长公共子串:在两个字符串中找到一个最长的公共子串,要求子串在原串中是连续的。

最长公共子序列:在两个字符串中找到一个最长的公共子串,不要求子串在原串中是连续的。

f[i][0]=f[0][i]=0;
for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++){
		f[i][j]=max(f[i][j],f[i-1][j]);
		f[i][j]=max(f[i][j],f[i][j-1]);
		if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
	}
cout<<f[n][m];

LCIS最长公共上升子序列

https://www.cnblogs.com/WArobot/p/7479431.html

cnblogs.com/Howe-Young/p/5082611.html

题目:

守望者的逃离

贪心。。。然鹅我只70pts

能闪现肯定闪现

不能闪我讨论了好几种

放个傻代码记录一下

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int m,s,t;
int ans;
bool f;
int main() {
    scanf("%d%d%d",&m,&s,&t);
    int ans=t,ss=s;
    while(s>0){
        while(m>=10&&t>0){
            if(s<=0) break;
            s-=60,m-=10,t--;
        } 
        while(m>=6&&s>17&&t>=2) {
        	t-=2;s-=60;m=m+4-10;
        	if(s<=0) break;
		}
        while(m>=2&&s>34&&t>=3){
        	m+=2,t-=3,s-=60;
        	if(s<=0) break;
		} 
        if(s>=120&&t>=7)  {s-=120;t-=7;}
        while(t>0&&s<120) {
            if(s<=0) break;
            s-=17,t--;
        }
        if(t<=0&&s>0) {f=1;break;}
    } 
    if(f) {
        printf("No
");
        printf("%d
",ss-s);
    } else {
        printf("Yes
");
        printf("%d
",ans-t);
    }
    return 0;
}

题解巧妙地方法是把闪现和跑步分着存距离——谁大就要谁

然后枚举时间

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int m,s,t;
int s1,s2;
bool f;
int main() {
    scanf("%d%d%d",&m,&s,&t);
    for(int i=1;i<=t;i++) {
    	s1+=17;
    	if(m>=10) {s2+=60,m-=10;}
    	else m+=4;
    	if(s2>s1) s1=s2;
    	if(s1>s) {
    		puts("Yes");
    		printf("%d
",i);
    		return 0;
		} 
	}
	puts("No"); 
	printf("%d
",s1);
    return 0;
}

dp思想也很好理解

#include <cstdio>
#include <algorithm>
using namespace std;
int dp[300001];
int main()
{
    int m, s, t;
    scanf("%d%d%d", &m, &s, &t);
    for (int i = 1; i <= t; i++)
    { 
        if (m >= 10)
            dp[i] = dp[i - 1] + 60, m -= 10; 
        else
            dp[i] = dp[i - 1], m += 4; 
    }
    for (int i = 1; i <= t; i++)
    {
        dp[i] = max(dp[i], dp[i - 1] + 17); 
        if (dp[i] >= s)
        {
            printf("Yes
%d", i);
            return 0;
        } 
    }
    printf("No
%d", dp[t]); 
    return 0;
}

乌龟棋

一开始想着开 5 维,当场爆炸,然后又去写了爆搜

30骗到手

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int x[355],y[355];
int ans,res;
void dfs(int pos) {
	if(pos==n) {ans=max(ans,res);return;}
	if(pos>n) return; 
	for(int i=1;i<=4;i++){
		if(y[i]==0) continue;
		pos+=i;y[i]--;res+=x[pos];
		dfs(pos);
		res-=x[pos];pos-=i;y[i]++;
	}
	return;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&x[i]);
	for(int i=1,u;i<=m;i++){
		scanf("%d",&u);
		y[u]++;
	}
	dfs(1);
	printf("%d
",x[1]+ans);
	return 0;
}

然后刚点开题解就发现4维就可以。。。毕竟题目说最后肯定能到n

想啊想,终于想到了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,m;
int x[355],y[355];
int ans,res;
int f[41][41][41][41];
inline void Max(int &x,int y){if(x<y) x=y;}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&x[i]);
	for(int i=1,u;i<=m;i++){
		scanf("%d",&u);
		y[u]++;
	}
	f[0][0][0][0]=x[1];
	for(int i=0;i<=y[1];i++) {
		for(int j=0;j<=y[2];j++) {
			for(int k=0;k<=y[3];k++) {
				for(int l=0;l<=y[4];l++) {
					int add=x[i+j*2+k*3+l*4+1];
					if(i>0) Max(f[i][j][k][l],f[i-1][j][k][l]+add);
					if(j>0) Max(f[i][j][k][l],f[i][j-1][k][l]+add);
					if(k>0) Max(f[i][j][k][l],f[i][j][k-1][l]+add);
					if(l>0) Max(f[i][j][k][l],f[i][j][k][l-1]+add);
				}
			}
		}
	}
	printf("%d
",f[y[1]][y[2]][y[3]][y[4]]);
	return 0;
}

原文地址:https://www.cnblogs.com/ke-xin/p/13499712.html