动态规划

数塔

dp[i][j] = max(dp[i+1][j],dp[i+1][j+1])+f[i][j];

最大连续子序列和

dp[i]记录以A[i]为末尾的连续序列最大和。
状态转移方程:dp[i] = max{A[i], dp[i-1]+A[i]}

#include<stdio.h>
int n,data[1010],dp[1010];
int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i=0;i<n;i++){
            scanf("%d",&data[i]);
        }
        dp[0] = data[0];
        int ans=-1;
        for(int i=0;i<n;i++){
            if(dp[i-1]+data[i]>data[i]){
                dp[i] = dp[i-1]+data[i];
            }
            else{
                dp[i] = data[i];
            }
            if(dp[i]>ans)
                ans = dp[i];
        }
        printf("%d\n",ans);

    }
    return 0;
}

最长不下降子序列

dp[i]表示以A[i]结尾的最长不下降子序列长度。
状态转移方程
dp[i] = max{1,dp[j]+1} (j=1,2,…,i-1&&A[j]<A[i])
边界:dp[i] = 1(1<=i<=n)

#include<stdio.h>
#include<string.h>
int main(){
    int A[100],l;//n记录数组长度,A[n]记录数据
    int dp[100] = {0};
    while(scanf("%d",&l)!=EOF){
        for(int i=0;i<l;i++){
            scanf("%d",&A[i]);
        }
        int ans=-1;
        for(int i=0;i<l;i++){
            dp[i]=1;
            for(int j=0;j<i;j++){
                if(A[i]>=A[j]&&dp[i]<dp[j]+1){
                    dp[i] = dp[j] + 1;
                    if(dp[i]>ans){
                        ans=dp[i];
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

最长公共子序列

dp[i][j]记录字符串A的i号位和字符串B的j号位之前的LCS长度
(注意解法中特殊的输入技巧

状态转移方程:  
			dp[i-1][j-1] + 1;A[i]==B[j]		
			//A,B串不同i-1,j-1
dp[i][j] = 
			max{dp[i-1][j],dp[i][j-1]};A[i]!=B[j]			

边界:dp[i][0] = dp[0][j] = 0 (0<=i<=n,0<=j<=m)
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int dp[100][100];
char A[100],B[100];
int main(){
    while(scanf("%s%s",A+1,B+1)!=EOF){//特殊技巧A+1 从第1个元素开始读取
        int l1=strlen(A+1),l2=strlen(B+1);//计算长度时也从1开始 所以要循环到length才可
        for(int i=0;i<=l1;i++)
            dp[i][0] = 0;
        for(int j=0;j<=l2;j++)
            dp[0][j] = 0;
        for(int i=1;i<=l1;i++){
            for(int j=1;j<=l2;j++){
                if(A[i]==B[j])
                    dp[i][j] = dp[i-1][j-1] + 1;
                else{
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        printf("%s %s %d\n",A+1,B+1,dp[l1][l2]);

    }
    return 0;
}

最长回文子串

dp[i][j]表示s[i]至s[j]所表示的串是否为回文串,是则为1不是则为0。

状态转移方程:
			dp[i+1][j-1],s[i]==s[j]
			//满足看子串
dp[i][j] = 
			0,s[i]!=s[j]
边界: dp[i][i] = 1,dp[i][i+1] = (s[i] == s[i+1])?1:0;

特殊递归写法:
按子串长度和子串初始位置进行枚举,及第一遍求长度为3的子串,第二遍求长度为4的子串。。。

//s为串
int len = strlen(s),ans=1;
memset(dp,0,sizeof(dp));//dp数组初始化为0
//边界
for(int i=0;i<n;i++){
	dp[i][i] = 1;
	if(i<len-1){
		if(s[i]==s[i+1]){
			dp[i][i+1] = 1;
			ans = 2;
		}
	}
}
//状态转移方程
for(int L=3;L<=len;L++){//枚举子串长度
	for(int i=0;i+L-1<len;i++){//枚举子串的起始端点
		int j=i+L-1;//子串的右端点
		if(s[i] == s[j] && dp[i+1][j-1] == 1){
			dp[i][j] = 1;
			ans = L;
		}
	}
	cout<<ans<<endl;
}

或者

#include<stdio.h>
#include<string.h>
int dp[100][100];
int main(){
    char str[100];
    int len,ans;
    memset(dp,0,sizeof(dp));
    while(scanf("%s",str)!=EOF){
        len = strlen(str);
        for(int i=0;i<len;i++){
            dp[i][i]=1;
            ans=1;
        }
        for(int i=0;i<len-1;i++){
            if(str[i]==str[i+1]){
                dp[i][i+1] = 1;
                ans=2;
            }
            else{
                dp[i][i+1] = 0;
            }
        }
        for(int l=3;l<=len;l++){
            for(int i=0;i+l-1<len;i++){
                int j=i+l-1;
                if(str[i]==str[j]&&dp[i+1][j-1]==1){
                    dp[i][j] = 1;
                    ans = l;
                }
                else{
                    dp[i][j] = 0;
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

背包

有一个容量为v的背包和一些物品。这些物品分别有两个属性,体积为w和价值为v。要求用这个背包装下价值尽可能多的物品,求其最大价值。

01背包问题(有穷件

dp[i][j]表示总体积不超过j的情况下,前i个物品能达到的最大价值。
状态转移方程:
dp[i][v] = max{dp[i-1][v],dp[i-1][v-w[i]]+c[i]};
//max(不装入该物品所能达到的最大价值,装入该物品所能达到的最大价值);
特别注意判断v-w[i]是否为负,若是,则不能被转移。
边界:dp[0][v] = 0;(0<=v<=V)
注意:每个状态只和上一个状态有关,特别进行空间优化

dp[v] = max{dp[v],dp[v-w[i]]+c[i]}
变为一维数组后,为了保证使用数据不变(该物品只能被取一次,枚举方向必须逆序
代码如下:

#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=100;
int dp[maxn];
struct thing{
    int u;//体积
    int w;//质量
};
thing t[maxn];
int main(){
    int data[maxn];
    int v,n;//n为数组长度
    while(scanf("%d%d",&n,&v)!=EOF){
        for(int i=0;i<n;i++){
            scanf("%d%d",&t[i].u,&t[i].w);
        }
        for(int i=0;i<v;i++){
            dp[i] = 0;
        }
        for(int i=0;i<n;i++){
            for(int j=v;j>=t[i].u;j--){
                dp[j] = max(dp[j],dp[j-t[i].u]+t[i].w);
            }
        }
        printf("%d\n",dp[v]);
    }
    return 0;
}

形成自己的解题风格
struct thing{
int v;
int w;
}
记录物品信息
0-1背包变化问题: 所选择的物品必须恰好装满背包
此时dp[i][j]必须保证体积和为j
解决方法:
改变初始状态
初始状态变为dp[0][0]为0,而其他dp[0][j]均变为负无穷。

理由如下:(观看某大神解
由f[j]=Math.max(f[j],f[j-w[i]]+v[i])知,第i步的最优解都是根据第i-1步推得,要想第i步获得的结果恰好装满,那么第i-1步也必须是恰好装满的最优解,否则第i-1的解也应该为Integer.minValue,因为Integer.minValue+W[i]=Integer.minValue。

完全背包(无穷件

状态转移方程:
dp[i][v] = max(dp[i-1][v],dp[i][v-w[i]]+c[i]);
边界:dp[0][v] = 0;
改成一维形式:
dp[v] = max(dp[v],dp[v-w[i]]+c[i]);
边界:dp[v] = 0;
此时有无穷件可取,所以正向枚举即可

for(int i=1;i<=n;i++{
	for(int v=w[i];v<=V;v++){
		dp[v] = max(dp[v],dp[v-w[i]]+c[i]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值