AcWing算法提高课 区间dp / 通过取模将数据规整到某区间 / 环形dp处理

区间dp常用的实现方式有:迭代式和记忆化搜索式。

 

迭代式可以枚举左右端点,也可以考虑先枚举区间长度,这样小区间就会在大区间之前被计算。大区间通过分界点由小区间转移。

例题 

AcWing 282. 石子合并

此题直接枚举区间长度和左端点,右端点不超过n。

AcWing 1068. 环形石子合并

此题可以考虑枚举区间长度和左端点,由于是环形数组,右端点可以越界,通过取模可以找到正确位置。

  注意在枚举右端点和枚举中间点时均需要判断是否越界,如果越界需要取模。

  如果区间为0-9,对num处理越界可以num%10;如果区间为1-9,对num处理越界可以(num-1)%9+1;

  区间为m-n 可以(num-m)%(n-m+1)+m;

  区间长度是多少就对多少取模,且区间右端点恰好不被取模影响,最后加上偏移。

复制代码
#include<bits/stdc++.h>
using namespace std;
//0-9可以mod10,1-9可以(i-1)%9+1
int nums[210];
int dp[210][210];
int psum[210];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>nums[i];
    for(int i=1;i<=n;i++) psum[i]=psum[i-1]+nums[i];
    memset(dp,0x3f,sizeof(dp));
    for(int i=1;i<=n;i++) dp[i][i]=0;
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=n;i++)
        {
            int j=i+len-1;
            int cpsum;
            if(j<=n)
            {
                cpsum=psum[j]-psum[i-1];
            }
            else
            {
                cpsum=psum[n]-psum[i-1]+psum[(j-1)%n+1];
            }
            for(int k=i;k<j;k++)
            {
                dp[i][(j-1)%n+1]=min(dp[i][(j-1)%n+1],dp[i][(k-1)%n+1]+dp[(k)%n+1][(j-1)%n+1]+cpsum);
            }
            
        }
    }
    int res=dp[1][n];
    for(int i=2;i<=n;i++) res=min(res,dp[i][i-1]);
    cout<<res<<endl;
    
    
    memset(dp,0,sizeof(dp));

    for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=n;i++)
        {
            int j=i+len-1;
            int cpsum;
            if(j<=n)
            {
                cpsum=psum[j]-psum[i-1];
            }
            else
            {
                cpsum=psum[n]-psum[i-1]+psum[(j-1)%n+1];
            }
            for(int k=i;k<j;k++)
            {
                dp[i][(j-1)%n+1]=max(dp[i][(j-1)%n+1],dp[i][(k-1)%n+1]+dp[(k)%n+1][(j-1)%n+1]+cpsum);
            }
            
        }
    }
    res=dp[1][n];
    for(int i=2;i<=n;i++) res=max(res,dp[i][i-1]);
    cout<<res<<endl;
    
}
View Code
复制代码

 

或者可以考虑在原数组尾部再接一个原数组,获得长度为2n的数组,并用282石子合并的方法求解。这也是环形dp的常用解法。

 

AcWing 1069. 凸多边形的划分 (此题还需要结合高精度,或者用int128,或者用python)

此题可以转化为与282类似的区间合并问题。因为三角形之间不会相交,可以枚举区间左右端点以及中间点所成的三角形进行区间dp。

复制代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int M=35;
LL dp[55][55][35];
LL nums[55];

void add(LL a[],LL b[])
{
    static LL c[M];
    memset(c,0,sizeof(c));
    LL carry=0;
    for(int i=0;i<M;i++)
    {
        carry+=a[i]+b[i];
        c[i]=carry%10;
        carry/=10;
    }
    memcpy(a,c,sizeof(c));
}

void mul(LL a[],LL b)
{
    static LL c[M];
    memset(c,0,sizeof(c));
    LL carry=0;
    for(int i=0;i<M;i++)
    {
        carry+=a[i]*b;
        c[i]=carry%10;
        carry/=10;
    }
    memcpy(a,c,sizeof(c));
}

bool cmp(LL a[],LL b[])
{
    for(int i=M-1;i>=0;i--)
    {
        if(a[i]<b[i])
            return true;
        if(a[i]>b[i])
            return false;
    }
    return false;
    
    
}
int main()
{
    int n;
    cin>>n;
    
    for(int i=1;i<=n;i++) cin>>nums[i];
    

    LL tmp[M];
    for(int len=3;len<=n;len++)
    {
        for(int l=1;l+len-1<=n;l++)
        {
            
            int r=l+len-1;
            dp[l][r][M-1]=1;
            for(int k=l+1;k<r;k++)
            {
                memset(tmp,0,sizeof(tmp));
                tmp[0]=1;
                mul(tmp,nums[l]);
                mul(tmp,nums[k]);
                mul(tmp,nums[r]);
                add(tmp,dp[l][k]);
                add(tmp,dp[k][r]);
                if(cmp(tmp,dp[l][r]))
                {
                    memcpy(dp[l][r],tmp,sizeof(tmp));
                }
                //dp[l][r]=min(dp[l][r],dp[l][k]+dp[k][r]+nums[l]*nums[k]*nums[r]);
            }
        }
    }
    int s=M-1;
    while(s>=0&&dp[1][n][s]==0) s--;
    for(int i=s;i>=0;i--) cout<<dp[1][n][i];
    
    cout<<endl;
}
View Code
复制代码
posted @   80k  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示