首页 > 其他分享 >动态规划dp

动态规划dp

时间:2023-05-24 13:13:18浏览次数:25  
标签:std main int namespace cin using 动态 规划 dp

///关于下标问题,当在计算时运用到i-1的时候,可以使用i从1开始,就没有越界的风险
///如果没有,一般从0开始比价好;
1.要想明白动态规划路线 ->第一步写出动态集合,第二步开始动态计算;
    1-1 0-1背包问题:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;//物品数量,最多载重;
int v[N],w[N];//物体的价值,重量;
int dp[N][N];//二维数组:表示存放第几个物品时,此时的总重量;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>w[i]>>v[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)//重量从0-m;
        {
            dp[i][j]=dp[i-1][j];
            if(j>=w[i])
                dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+v[i]);
        }
    }
    cout<<dp[n][m]<<endl;
    return 0;
}
1-1 0-1背包优化:一维数组
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;//物品数量,最多载重;
int v[N],w[N];//物体的价值,重量;
int dp[N];//二维数组:表示存放第几个物品时,此时的总重量;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>w[i]>>v[i];
    for(int i=1;i<=n;i++)
        for(int j=m;j>=w[i];j--)//重量从0-m;
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    cout<<dp[m]<<endl;
    return 0;
}
1-3 0-1背包输出背包
///难点在于如何输出将哪个物品装入背包,可利用回溯法进行实现
///更加考研对背包和动态规划的理解
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int n,m;
int v[N],w[N],f[N][N],x[N];///x[i]标记用过的背包;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>v[i];
    for(int i=1;i<=n;i++)
        cin>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
        {
            f[i][j]=f[i-1][j];
            if(j>=w[i])
            f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);///普通0-1背包;
            /// 每一个j都对应一个状态;后面回溯会根据这个状态来进行判断;
        }
    cout<<"Optimal value is"<<endl;
    cout<<f[n][m]<<endl;
    for(int i=n;i>=1;i--)///利用回溯,从后向前走;
    {
        if(f[i][m]!=f[i-1][m])///如果两者相等,也就是在i背包和i-1背包之间没有装任何物品;
        {
            m-=w[i];
            x[i]=1;
        }
    }
    for(int i=1;i<=n;i++)
        cout<<x[i]<<" ";
    return 0;
}
2-1 完全背包
与0-1背包不同,完全背包里物品可以多次使用,但大体思路相同;
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int dp[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>w[i]>>v[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k*w[i]<=m;k++)
            dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]+k*v[i]);
    cout<<dp[n][m]<<endl;
    return 0;
}
2-2 完全背包优化:
    1.根据分析,可以发现在循环时,如果是dp[i][j-w[i]]开头的话,后续的循环与dp[i][j]对比
    两者的关系是dp[i][j]=dp[i][j-w[i]]+v[i];
    故我们可以少一层k循环;
    2.利用0-1背包思路,优化成一维数组.
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int dp[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>w[i]>>v[i];
    for(int i=1;i<=n;i++)
        for(int j=w[i];j<=m;j++)///这里不需要逆序,因为0-1是看i-1的状态,而完全背包是i状态,不需要改变.
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    cout<<dp[m]<<endl;
    return 0;
}
///看好0-1背包和完全背包问题非常的相似,想清楚这两者只差一行代码的区别在哪!!!!
3 多重背包问题-暴力
本质与完全背包问题没有区别,只是k循环最多循环到每个物品最高限制次,即s[i]次;
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int dp[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>w[i]>>v[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k<=s[i]&&k*v[i]<=j;k++)//就多出来一个判断条件;
            dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]+k*v[i]);
    cout<<dp[n][m]<<endl;
    return 0;
}
3-2 多重背包问题-优化
详解://https://blog.csdn.net/weixin_72060925/article/details/128404378?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-128404378-blog-109363826.235%5Ev30%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-128404378-blog-109363826.235%5Ev30%5Epc_relevant_default_base3&utm_relevant_index=2
#include<bits/stdc++.h>
using namespace std;
const int N=25000,M=1010;
int n,m;
int v[N],w[N];
int dp[N];
int main()
{
    cin>>n>>m;
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        int a,b,s;
        cin>>a>>b>>s;
        int k=1;
        while(k<=s)//使用二进制计数法,例如s=200,那么我们的k就可以循环到64,此时可以表示出1-128的所有数字
        {                  //那么剩下的72个怎么表示呢,剩下的72个
            cnt++;
            w[cnt]=k*a;
            v[cnt]=k*b;
            s-=k;
            k*=2;
        }
        if(s>0)
        {
            cnt++;
            w[cnt]=a*s;
            v[cnt]=b*s;
        }
    }
    n=cnt;//更新n的值,接下来就当0-1背包问题做就可以;
    for(int i=1;i<=n;i++)
        for(int j=m;j>=w[i];j--)
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    cout<<dp[m]<<endl;
    return 0;
}


4-1 多组背包
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
int w[N][N],v[N][N],s[N];
int dp[N];
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        cin>>s[i];
        for(int j=0;j<s[i];j++)
            cin>>w[i][j]>>v[i][j];
    }
    for(int i=0;i<n;i++)
        for(int j=m;j>0;j--)
        for(int k=0;k<s[i];k++)
        if(j>=w[i][k])
            dp[j]=max(dp[j],dp[j-w[i][k]]+v[i][k]);//还是老方法,就是多了重循环哈
        cout<<dp[m]<<endl;
        return 0;
}
5- 依赖背包,存在主附,要买附就要现有主
//P1064 [NOIP2006 提高组] 金明的预算方案
//链接:https://www.luogu.com.cn/problem/P1064
///当时做这个题时,满脑子都是思路,一遍遍确认一遍遍不对,最后的大体思路是对了
///但没想到分情况讨论.
///依赖背包在附件少的情况下完全可以分情况讨论,等我再看看算法,应该有个通用方法.
#include<iostream>
using namespace std;
int m,n,mw[33333],mv[33333],fw[33333][3],fv[33333][3],f[33333],v,p,q;
//mw主件重量,mv主件价值,fw主件对应的附件重量,fv主副价值,n总重量,m总个数
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++){
    cin>>v>>p>>q;
    if(!q){//如果是主件
        mw[i]=v;//主件重量
        mv[i]=v*p;//主件价值与重量乘积
    }
    else{//如果是附件
        fw[q][0]++;//记录主件的附件个数(只记录在fw就行,fv那里没用
        fw[q][fw[q][0]]=v;//主件的个数是用来确定该附件应该填在第一个还是第二个格子里
        fv[q][fw[q][0]]=v*p;//(是第一个还是第二个附件)
    }
    }
    for(int i=1;i<=m;i++)
    for(int j=n;j>=mw[i];j--){//01背包模板
    //每一个if的前提是背包能不能装下该物品
        //情况1:只要主件 和啥都不要比较
        f[j]=max(f[j],f[j-mw[i]]+mv[i]);
        //情况2:主件和附件1 和上面选出的较大值比较
        if(j>=mw[i]+fw[i][1])f[j]=max(f[j],f[j-mw[i]-fw[i][1]]+mv[i]+fv[i][1]);
        //情况3:主件和附件2 和上面选出的较大值比较
        if(j>=mw[i]+fw[i][2])f[j]=max(f[j],f[j-mw[i]-fw[i][2]]+mv[i]+fv[i][2]);
        //情况4:都要
        if(j>=mw[i]+fw[i][1]+fw[i][2])
        f[j]=max(f[j],f[j-mw[i]-fw[i][1]-fw[i][2]]+mv[i]+fv[i][1]+fv[i][2]);
    }
    //输出在价值为n时能得到的最大值
    cout<<f[n]<<endl;
    return 0;
}
///线性dp
6-数字三角形
#include<bits/stdc++.h>
#define INF -1e9
using namespace std;
const int N=505;
int n,a[N][N],f[N][N];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
        cin>>a[i][j];
    for(int i=0;i<=n+1;i++)
        for(int j=0;j<=i+1;j++)
        f[i][j]=INF;
    f[1][1]=a[1][1];//一定要记得从起点开始初始化,不然会变成负无穷
    for(int i=2;i<=n;i++)//一定要从第二层开始,如果从第一层开始,第一次会访问到负无穷区域;
        for(int j=1;j<=i;j++)
        f[i][j]=max(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j]);
    int res=INF;
    for(int i=1;i<=n;i++)
        res=max(res,f[n][i]);
    cout<<res;
    return 0;
}
7-1 最长上升/下降 子序列
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n;
int a[N],f[N];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=n;i++)
    {
        f[i]=1;//每个i位置初始都是1,没循环之前就a[i]一个数字
        for(int j=1;j<i;j++)
            if(a[i]>a[j])//如果i位置比之前的大
                f[i]=max(f[i],f[j]+1);
    }
    int res=-1;
    for(int i=1;i<=n;i++)
        res=max(res,f[i]);
    cout<<res;
}
7-2 最长子序列输出顺序
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n;
int a[N],g[N],f[N];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=n;i++)
    {
        f[i]=1;
        g[i]=0;
        for(int j=1;j<i;j++)
            if(f[i]<f[j]+1)
        {
            f[i]=f[j]+1;
            g[i]=j;
        }
    }
    int res=0,k=0;
    for(int i=1;i<=n;i++)
        if(f[i]>res)
    {
        res=f[i];
        k=i;
    }
    cout<<res<<endl;
    for(int i=0,len=f[k];i<len;i++)
    {
        cout<<a[k]<<' ';
        k=g[k];
    }
    return 0;
}
7-3 最长上升子序列优化
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int res=0,x,a[N],q[N],q1[N],len=0;
int main()
{
    while(cin>>x) a[res++]=x;
    q[0]=2e9;
    q1[0]=-2e9;
    for(int i=0;i<res;i++)
    {
        if(q[len]>=a[i]) q[++len]=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<<len<<endl;//最长不上升
    len=0;
     for(int i=0;i<res;i++)
    {
        if(q1[len]<a[i]) q1[++len]=a[i];
        else
        {
          int l=0,r=len;
          while(l<=r)
          {
              int mid=l+r>>1;
              if(q1[mid]>=a[i]) r=mid-1;
              else l=mid+1;
          }
          q1[l]=a[i];
        }
    }
    cout<<len;//最长上升
    return 0;
}

8-1 最长公共子序列
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
char a[N],b[N];
int f[N][N];
int main()
{
    cin>>n>>m;
    cin>>a+1>>b+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
    {
        f[i][j]=max(f[i-1][j],f[i][j-1]);///状态转移说简单些就是做选择,比如说这个问题,是求 s1 和 s2 的最长公共子序列,不妨称这个子序列为 lcs.
    ///那么对于 s1 和 s2 中的每个字符,有什么选择?很简单,两种选择,要么在 lcs 中,要么不在。
    ///如果某个字符应该在 lcs 中,那么这个字符肯定同时存在于 s1 和 s2 中;
    ///如果 s1[i]==s2[j],那么这个字符一定在 lcs 中;否则的话,s1[i] 和 s2[j] 这两个字符至少有一个不在 lcs 中,需要丢弃一个.
    ///保留一个的原因是,i从小到大遍历,i后面有可能会出现与这时保留相同的字节
        if(a[i]==b[j])
            f[i][j]=max(f[i][j],f[i-1][j-1]+1);
    }
    cout<<f[n][m];
}
8-2 最长公共子序列优化:  https://www.luogu.com.cn/problem/P1439
/// 运用离散化来解决问题;
///我们可以以第一个串为标准,用第二个串来匹配第一个串,看能匹配多少,所以,其实第一个串的每个数字其实影响不大,只有知道它对应了第二串的哪个数字就好了,那么我们为什么不把他给的串重新定义一下?
///比如他的样例:3 2 1 4 5 我们把他变成 1 2 3 4 5 用一个数组记录一下每个数字变成了什么,相当于离散化了一下3-1;2-2;1-3;4-4;5-5;
///现在我们的第二串1 2 3 4 5 按我们离散化的表示:3 2 1 4 5
///可能有些人已经懂了,我们把第一个串离散化后的数组是满足上升,反过来,满足上升的也就是满足原串的排列顺序的,(如果你不懂的话可以多看几遍这个例子)O(∩_∩)O~
///好了 ,现在的问题就变成了求一个最长不下降序列!好了!解决完成!
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n;
int a[N],b[N],belong[N+10],q[N],len=0;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            belong[a[i]]=i;
        }
    for(int i=1;i<=n;i++)
        cin>>b[i];
    q[0]=-2e9;
    for(int i=1;i<=n;i++)
    {
        if(belong[b[i]]>q[len])
        {
            q[++len]=belong[b[i]];
            continue;
        }
        else
        {
            int l=0,r=len;
            while(l<r)
            {
                int mid=l+r>>1;
                if(q[mid]<belong[b[i]]) l=mid+1;
                else r=mid;
            }
            q[l]=belong[b[i]];
        }
    }
    cout<<len;
    return 0;
}
9-1 区间DP
石子合成问题://https://blog.csdn.net/qq_45069496/article/details/123159802?ops_request_misc=&request_id=&biz_id=102&utm_term=%E7%9F%B3%E5%AD%90%E5%90%88%E5%B9%B6%E9%97%AE%E9%A2%98%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-123159802.142^v86^control,239^v2^insert_chatgpt&spm=1018.2226.3001.4187
区间dp难点在于循环,个人认为
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n;
int s[N],f[N][N];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>s[i];
    for(int i=1;i<=n;i++)
        s[i]+=s[i-1];
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i+len-1<=n;i++)
        {
            int j=i+len-1;
            for(int k=i;k<j;k++)
                f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
        }
    }
    cout<<f[1][n];
    return 0;
}
9-2 用单调队列实现动态规划,优化效率
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int use[2*N],l,r,n,hh,tt,q[N],a[2*N],f[2*N],res=-2e9;
int main()
{
    cin>>n>>l>>r;
   for(int i=1;i<=2*n;i++)
    f[i]=-1e9;
    for(int i=l;i<=r;i++) use[i]=1;///标记,当i循环到l,就是第一步
    for(int i=0;i<=n;i++)
        cin>>a[i];
    for(int i=0;i<=n;i++)
    {
        while(hh<=tt&&q[hh]<i-r+l) hh++;///检查是否出列
        while(hh<=tt&&f[i]>f[q[tt]]) tt--;///队尾出列,维护最大值;
        q[++tt]=i;
        f[i+l]=f[q[hh]]+a[i+l];
        if(use[i]) use[i+l]=1;///标记作用,
    }
    for(int i=n+1;i<=n+l;i++)
        if(use[i]) res=max(res,f[i]);
    cout<<res;
    return 0;
}
/**
    https://www.luogu.com.cn/problem/CF414B
    本题考察思维,状态表示为f[i][j],i是当前的长度,j是最大的数
    那么此时本题有两种说法,第一种:f[i][j]+=f[i-1][j|k],即k是j的因数
    第二种方法是:f[i+1][j]+=f[i][k|j],即k是j的倍数;
    第一种方法可以看出来,我们需要枚举出j所有的因数,时间复杂度较高
    而第二种方法,我们则可以通过k+=j来实现
**/
#include<bits/stdc++.h>
using namespace std;
const int N=2020,mod=1000000007;
int n,k,f[N][N];
long long res;
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++) f[1][i]=1;
    for(int i=1;i<=k-1;i++)
        for(int j=1;j<=n;j++)
            for(int k=j;k<=n;k+=j)
            {
                f[i+1][k]+=f[i][j];///这样一来每一次,k都是j的倍数,
                f[i+1][k]%=mod;///每一步都要取模
            }
    for(int i=1;i<=n;i++)
    {
        res+=f[k][i];
        res%=mod;
    }
    cout<<res;
    return 0;
}
/**
    https://www.luogu.com.cn/problem/P1077
    芜~,这次状态想对了,状态转移方程也是差不多,但是呢,在处理k的时候出问题了
    看了题解才会,唉,任重而道远啊;
**/
#include<bits/stdc++.h>
using namespace std;
const int N=2020,mod=1e6+7;
int n,m,f[N][N],a[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    f[0][0]=1;
    for(int j=0;j<=m;j++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int k=0;k<=min(j,a[i]);k++)
              f[i][j]=(f[i][j]+f[i-1][j-k])%mod;
        }
    }
    cout<<f[n][m];
    return 0;
}
/**
    https://www.luogu.com.cn/problem/P1115
    我无语啦,为啥就是要看题解,好好想想啊喂;
    枚举每个从i开始,左到右的最长不上升子序列就OK了;
**/
#include<bits/stdc++.h>
using namespace std;
const int N=500;
int n,zuo[N],you[N],res,a[N];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        zuo[i]=1;///初始化放在哪都可以;
        you[i]=1;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
            if(a[i]>a[j])
                zuo[i]=max(zuo[i],zuo[j]+1);
    for(int i=n;i>=1;i--)
        for(int j=i+1;j<=n;j++)
            if(a[i]>a[j])
                you[i]=max(you[i],you[j]+1);
    for(int i=1;i<=n;i++)
        res=max(res,zuo[i]+you[i]-1);///我怎么感觉这是贪心(恼)
    cout<<n-res;
    return 0;
}
/**
    https://www.luogu.com.cn/problem/P1510
    气死我啦,又是背包问题,关键是还不会,哎哟,烦死了
    估计今天状态不对
**/
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int n,m,t,f[N],w[N],v[N];
int main()
{
    cin>>m>>n>>t;
    for(int i=1;i<=n;i++) cin>>w[i]>>v[i];
    for(int i=1;i<=n;i++)
        for(int j=t;j>=v[i];j--)///必须枚举体力,如果枚举体积的话,要开个sum,从sum开始遍历,会超时
            f[j]=max(f[j],f[j-v[i]]+w[i]);
    if(f[t]<m) cout<<"Impossible";
    else
    {
        int x=t;
        while(f[x]>=m) x--;///当第一个体积不大于m时,停止;
        cout<<t-x-1;
    }
    return 0;
}
/**
    https://www.luogu.com.cn/problem/P1018
    还是经典动态规划,详细思路放代码里
**/
#include<bits/stdc++.h>
using namespace std;
const int N=2020;
int n,k;
long long f[N][N],a[N],s[N][N];///a表示存储每一个数字,s表示从i开始,往后数j个位置代表的数(例:123456,s[1][3]=123)
///f表示第i位时,局部最优解是什么,k表示用乘法的次数;
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        char c;
        cin>>c;
        a[i]=c-'0';
    }
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            s[i][j]=s[i][j-1]*(long long)10+a[j];///首先表示出s数组,要存longlong
    for(int i=1;i<=n;i++) f[i][0]=s[1][i];///数组初始化
    for(int j=1;j<=k;j++)
        for(int i=1;i<=n;i++)
            for(int l=1;l<i;l++)
                f[i][j]=max(f[i][j],f[l][j-1]*s[l+1][i]);///从l为1开始遍历,寻找局部最优解,再反馈给最优解;
    cout<<f[n][k];
    return 0;
}
/**
    区间DP
    狠狠的拷打
**/
///区间dp1 石子合并:https://www.luogu.com.cn/problem/P1775
///这个就是典型的区间dp啊,要知道区间dp是枚举的每个区间,区间是1的那就是本身了,就不需要再枚举了
///如果时至今日还不会的话再去csdn或者acwing上看看课程吧qaq
///不说了我要手打一遍了
#include<bits/stdc++.h>
using namespace std;
const int N=2020;
int n,a[N],s[N],f[N][N];///f[i][j]表示从i到j合并这个区间所用的最小值;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        s[i]=s[i-1]+a[i];///求前缀和,因为我们要枚举每个区间,而每个区间的总和在这里要预处理一下
    }
    for(int len=2;len<=n;len++)///枚举区间,因为len=1的时候就是自己搬自己,不消耗体力,没必要枚举;
        for(int i=1;i+len-1<=n;i++)///区间左端点是i,区间右端点是i+len-1,所以右端点不能超过n
        {
            int l=i,r=i+len-1;
            f[l][r]=1e9;///因为要求最小值,所以先把这个区间值赋的很大;
            for(int k=l;k<r;k++)
                f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);///从l到k的合并区间+从k+1到r的合并区间,再加上合并这俩区间所需要的体力;
        }
    /**
        这里补充一个方法,可以从len=1开始;
        memset(f,0x3f,sizeof f);
        for(int len=2;len<=n;len++)
        for(int i=1;i+len-1<=n;i++)
        {
            int l=i,r=i+len-1;
            if(len==1) f[l][r]=0;///这里不同;
            else
            for(int k=l;k<r;k++)
            f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
        }
    **/
    cout<<f[1][n];///从1合并到n的最小值
    return 0;
}
///区间dp2-合并果子加强版:https://www.luogu.com.cn/problem/P1880
///没什么说法,想象一下,把这个环拆开,然后再补一个从1到n的链,那么我们每次断开的时候是不是就是从断点到区间那个长度?
///经典具体说法请看acwing,我要手打代码了呜呜呜
#include<bits/stdc++.h>
using namespace std;
const int N=2020;
int n,s[N],f[N][N],g[N][N],w[N];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>w[i];
        w[i+n]=w[i];
    }
    for(int i=1;i<=2*n;i++) s[i]=s[i-1]+w[i];
    for(int len=2;len<=n;len++)
        for(int i=1;i+len-1<=2*n;i++)
    {
        int l=i,r=i+len-1;
        f[l][r]=1e9,g[l][r]=-1e9;///注意,赋值一定要赋的足够大;
        for(int k=l;k<r;k++)
        {
            f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
            g[l][r]=max(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]);
        }
    }
    int res=2e6,ans=-2e6;///记录最小值最大值;
    for(int i=1;i<=n;i++)
    {
        res=min(res,f[i][i+n-1]);///枚举所有区间为n的,找出最小值;
        ans=max(ans,g[i][i+n-1]);
    }
    cout<<res<<endl<<ans;
    return 0;
}
/* 
    https://www.luogu.com.cn/problem/P1164
    经典的dp问题,我真是服了,一定要多想想这种类型的;
    一眼就是背包;
    先定义状态,脑袋里不能只有二维状态;
    f[i]表示有i元时的方案数;
    那么就可以转换为经典的背包问题了;
*/
#include<bits/stdc++.h>
using namespace std;
const int N=2020;
int n,m,w[N],f[N],res;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>w[i];
    f[0]=1;
    for(int i=1;i<=n;i++)
        for(int j=m;j>=w[i];j--) f[j]+=f[j-w[i]];
    cout<<f[m];
    return 0;
}

 

标签:std,main,int,namespace,cin,using,动态,规划,dp
From: https://www.cnblogs.com/o-Sakurajimamai-o/p/17427991.html

相关文章

  • 基于django前端页面动态菜单
    1、settings中定义基于中间件变量的字典UNICOM_MENU={'leader':[{'text':'用户管理','url':'/xx/xx/'},{'text':'订单管理','url':'/xx/xx/'},{'text......
  • Qt+QtWebApp开发笔记(三):http服务器动态html连接跳转基础交互
    前言  网页很多时候是动态的,于是本篇文章目标实现一个简答的动态页面—页静态页面互相跳转,点击可以跳转到子页面。 Demo  下载地址  链接:https://pan.baidu.com/s/1bbhcu1XTiaJRYGRQRG5a0g?pwd=1234 HTML基本页面交换  上一篇的“HelloWorld”......
  • CSI2协议-DPHY 数据格式包
       ......
  • 不同路径 II(数组、动态规划)、同构字符串(哈希表、字符串)、颠倒二进制位(位运算、分
    不同路径II(数组、动态规划)一个机器人位于一个_mxn_网格的左上角(起始点在下图中标记为“Start”)。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?网......
  • 蓝桥杯2022年第十三届决赛真题-斐波那契数组(动态规划)
    题目描述如果数组A=(a0,a1,···,an−1)满足以下条件,就说它是一个斐波那契数组:n≥2;a0=a1;对于所有的i(i≥2),都满足ai=ai−1+ai−2。现在,给出一个数组A,你可以执行任意次修改,每次修改将数组中的某个位置的元素修改为一个大于0的整数。请问最......
  • wordpress部署
    wordpress部署1,拉取镜像dockerpull192.168.100.10/library/wordpressdockerpull192.168.100.10/library/mysql:5.62,编写compose文件mkdir/root/wordpressvimdocker-compose.yamlversion:'3.3'services:wordprss:image:192.168.100.1......
  • 从数字三角形开始的DP生活——第一天
    题目链接#include<iostream>usingnamespacestd;constintN=1e3+5;intn;intf[N][N],a[N][N],ans;intmain(){ ios::sync_with_stdio(0); cin.tie(0);cout<<fixed; cin>>n; for(inti=1;i<=n;i++) for(intj=1;j<=i;j++) cin>>a......
  • UE4学习笔记:Windows系统下如何在C++项目里调用第三方动态库
    本随笔介绍在Windows系统下,由UE4引擎创建的C++项目里如何实现调用第三方动态库的方法。随笔作者还在学习阶段,对UE4引擎的使用和理解还不是非常透彻,难免会在随笔内容里出现技术上或书写上的问题,如果出现了类似的问题欢迎在评论区或者私信讨论。 目录设置第三方库头文件的路......
  • QT5中动态更改图标的方法(转)
    简述在做工程中遇到一个问题,需要根据程序的运行动态的改变显示的图标。在网上找了几篇博客,都失败了,后来自己看UI文件,发现了失败原因,就是设置图标的时候,输入的问文件路径有问题。我摸索出的方法如下。Step1:添加资源文件在工程文件处,右击鼠标》添加新文件》QTresource修改前缀名......
  • 注解中动态获取nacos值【attribute value must be constant】
    nacos中配置环境参数env:es:dev注解中添加参数信息@Data@IndexName(value="#{@envEs}")publicclassEsInfo{privateLongid;}添加配置文件获取配置数据@ComponentpublicclassEnvEsConfig{@Value("${env.es}")privateStringenvEs;@Be......