zoukankan      html  css  js  c++  java
  • 线性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;
    }
    
    
  • 相关阅读:
    Linux内核RPC请求过程
    二分图
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 文本加密
    Java实现 蓝桥杯 算法提高 文本加密
    Java蓝桥杯 算法提高 九宫格
    Java蓝桥杯 算法提高 九宫格
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13499712.html
Copyright © 2011-2022 走看看