DP-区间dp的一些题目

目录

题目1:合并回文子串

解题

题目2:取数游戏2

解题

矩阵取数游戏

解题

题目3:wyh的问题

解题

题目4:to the max

解题

题目5:棋盘制作

解题


题目1:合并回文子串

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

输入两个字符串A和B,合并成一个串C,属于A和B的字符在C中顺序保持不变。如"abc"和"xyz"可以被组合成"axbycz"或"abxcyz"等。
我们定义字符串的价值为其最长回文子串的长度(回文串表示从正反两边看完全一致的字符串,如"aba"和"xyyx")。
需要求出所有可能的C中价值最大的字符串,输出这个最大价值即可

解题

#include<iostream>
#include<cstring>
using namespace std;
const int N=55;
char a[N],b[N];
bool f[N][N][N][N];
int T;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",a+1);
        scanf("%s",b+1);
        int an=strlen(a+1),bn=strlen(b+1);
        int res=1;
        for(int len1=0;len1<=an;len1++)
            for(int len2=0;len2<=bn;len2++)
                for(int i=1;i+len1-1<=an;i++)
                    for(int k=1;k+len2-1<=bn;k++)
                    {
                        int j=i+len1-1,l=k+len2-1;
                        bool &u=f[i][j][k][l];
                        u=0;
                        if(len1+len2<=1) u=1;
                        else{
                            if(a[i]==a[j]) u|=f[i+1][j-1][k][l];
                            if(a[j]==b[k]) u|=f[i][j-1][k+1][l];
                            if(a[i]==b[l]) u|=f[i+1][j][k][l-1];
                            if(b[k]==b[l]) u|=f[i][j][k+1][l-1];
                        }
                        if(u) res=max(res,len1+len2);
                    }
        printf("%d\n",res);
    }
}

题目2:取数游戏2

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

给定两个长度为n的整数列A和B,每次你可以从A数列的左端或右端取走一个数。假设第i次取走的数为ax,则第i次取走的数的价值vi=bi⋅ax,现在希望你求出∑vi的最大值。

解题

这里要注意的是,在从短区间枚举到长区间的过程中,我们实际上是从最后一次取数枚举到第一次取数。也就是说,我们取到长度为1的区间时,对应的b值是bn。

#include<iostream>
#include<cstring>
using namespace std;
const int N=1010;
int T;
int a[N],b[N];
int f[N][N];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(f,0,sizeof(f));
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int j=1;j<=n;j++) scanf("%d",&b[j]);
        for(int k=1;k<=n;k++)
            for(int i=1;i+k-1<=n;i++)
            {
                int j=i+k-1;
                f[i][j]=max(f[i+1][j]+a[i]*b[n-k+1],f[i][j-1]+a[j]*b[n-k+1]);
            }
        printf("%d\n",f[1][n]);
    }
}

矩阵取数游戏

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数。游戏规则如下:
1.每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
2.每次取走的各个元素只能是该元素所在行的行首或行尾;
3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 * 2i,其中i表示第i次取数(从1开始编号);
4.游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

解题

在取数游戏2的基础上加了高精度。

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int N=85;
typedef long long ll;
int n,m;
ll a[N][N];
vector<ll> b[N];
vector<ll> add(vector<ll> A,vector<ll> B)
{
    vector<ll> C;
    ll t=0;
    for(int i=0;i<A.size()||i<B.size();i++)
    {
        if(i<A.size()) t+=A[i];
        if(i<B.size()) t+=B[i];
        C.push_back(t%10);
        t/=10;
    }
    if(t) C.push_back(1);
    return C;
}
vector<ll> mul(vector<ll> A,ll b)
{
    vector<ll> C;
    ll t=0;
    for(int i=0;i<A.size()||t;i++)
    {
        if(i<A.size()) t+=A[i]*b;
        C.push_back(t%10);
        t/=10;
    }
    return C;
}
vector<ll> get_max(vector<ll> A,vector<ll> B)
{
    if(A.size()>B.size()) return A;
    else if(A.size()<B.size()) return B;
    else{
        for(int i=A.size()-1;i>=0;i--) 
        {
            if(A[i]>B[i]) return A;
            else if(B[i]>A[i]) return B;
        }
    }
    return A;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=m;j++)
            scanf("%lld",&a[i][j]); 
     
    vector<ll> i;
    i.push_back(1);
    for(int j=0;j<=N;j++)
    {
        b[j]=i;
        i=mul(i,2);
    }
    vector<ll> res;
    for(int k=1;k<=n;k++)
    {
        vector<ll> f[N][N];
        for(int len=1;len<=m;len++)
            for(int i=1;i+len-1<=m;i++)
            {
                int j=i+len-1;
                f[i][j]=get_max(add(mul(b[m-len+1],a[k][i]),f[i+1][j]),add(mul(b[m-len+1],a[k][j]),f[i][j-1]));
            }
        res=add(res,f[1][m]);
    }
    for(int i=res.size()-1;i>=0;i--) printf("%lld",res[i]);
}

题目3:wyh的问题

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

我国现在能源消耗非常严重,现在政府有这样一个工作,每天早上都需要把一些路灯关掉,但是他们想让在关闭的过程中所消耗的能源是最少的,负责路灯关闭的工作人员以1m/s的速度进行行走,假设关闭路灯的时候不需要花费任何的时间,请你编写一个程序,计算在给定路灯位置和每个路灯的消耗能源的多少,求出当所有路灯关闭的时候所需要的最少能量

解题

与一般的区间dp问题相比较,在这道题目中,我们还需要知道人在上一区间中处在的位置。

实际上f[N][N][2]数组中,我们只利用到了一半的空间(j<i的空间都没有利用上),利用这个性质,我们可以用f[i][j]表示人在左端点,用f[j][i]表示人在右端点,就可以把我们的实现优化到2维。

#include<iostream>
#include<cstring>
using namespace std;
const int N=1010;
int n;
int f[N][N];
int d[N],w[N];
int main()
{
    while(cin>>n)
    {
        memset(f,0x3f,sizeof(f));
        memset(d,0,sizeof(d));
        memset(w,0,sizeof(w));
        int v;
        scanf("%d",&v);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&d[i],&w[i]);
            w[i]+=w[i-1];
        }
        f[v][v]=0;
        for(int i=v;i>=1;i--)
            for(int j=v;j<=n;j++)
            {
                f[i][j]=min(f[i][j],min(f[i+1][j]+(d[i+1]-d[i])*(w[n]-w[j]+w[i]),f[j][i+1]+(d[j]-d[i])*(w[n]-w[j]+w[i])));
                f[j][i]=min(f[j][i],min(f[j-1][i]+(d[j]-d[j-1])*(w[n]-w[j-1]+w[i-1]),f[i][j-1]+(d[j]-d[i])*(w[n]-w[j-1]+w[i-1])));
            }
        printf("%d\n",min(f[1][n],f[n][1]));
    }
}

题目4:to the max

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

Given a two-dimensional array of positive and negative integers, a sub-rectangle is any contiguous sub-array of size 1*1 or greater located within the whole array. The sum of a rectangle is the sum of all the elements in that rectangle. In this problem the sub-rectangle with the largest sum is referred to as the maximal sub-rectangle. 
As an example, the maximal sub-rectangle of the array: 

0 -2 -7 0 
9 2 -6 2 
-4 1 -4 1 
-1 8 0 -2 
is in the lower left corner: 

9 2 
-4 1 
-1 8 
and has a sum of 15. 

输入描述:

The input consists of an N*N array of integers. The input begins with a single positive integer N on a line by itself, indicating the size of the square two-dimensional array. This is followed by N2N^2N2 integers separated by whitespace (spaces and newlines). These are the N2N^2N2 integers of the array, presented in row-major order. That is, all numbers in the first row, left to right, then all numbers in the second row, left to right, etc. N may be as large as 100. The numbers in the array will be in the range [-127,127].

输出描述:

Output the sum of the maximal sub-rectangle.

解题

首先要想清楚的是,我们每次进行状态转移时,可以看作是在上一个状态的基础上加上一行(x属于[1,n],y1和y2固定的一行),也可以看作是加上一列(y属于[1,n],x2和x1固定的一列),在这里,我们以每次加上一列的情况为例。

将状态转移的情况简化后,在前缀和处理部分,我们只需要进行一个一维的前缀和就可以了。

for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) s[i][j]=s[i-1][j]+a[i][j];

dp思路如下:

要注意,在每次加上一列时,如果前面的总和是小于0的,我们其实可以直接舍弃掉前面的值,只取当前值。AC代码如下:

#include<iostream>
using namespace std;
const int N=105;
int n;
int a[N][N],s[N][N];
int f[N][N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) s[i][j]=s[i-1][j]+a[i][j];
   
    for(int x1=n;x1>=1;x1--)
        for(int x2=x1;x2<=n;x2++)
        {
            f[x1][x2]=max(f[x1+1][x2],f[x1][x2-1]);
            int last=0;
            for(int k=1;k<=n;k++)
            {
                last=max(last,0)+s[x2][k]-s[x1-1][k];
                f[x1][x2]=max(f[x1][x2],last);
            }
        }
    printf("%d",f[1][n]);
}

题目5:棋盘制作

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。

据说国际象棋起源 于易经的思想,棋盘是一个8*8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。

而我们的主人公小Q, 正是国际象棋的狂热爱好者。作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定将棋盘扩大以适应他们的新规则。

小Q找到了一张由N*M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一。小Q想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。

不过小Q还没有决定是找 一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。于是小Q找到了即将参加全国信息学竞赛的你,你能帮助他么?

解题

这道题采用悬线法。思路如下:

AC代码如下:

#include<iostream>
using namespace std;
const int N=2010;
int a[N][N];
int l[N][N],r[N][N],u[N][N];
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            l[i][j]=r[i][j]=j;
            u[i][j]=1;
        }
    for(int i=1;i<=n;i++)
        for(int j=2;j<=m;j++)
            if(a[i][j-1]!=a[i][j]) l[i][j]=l[i][j-1];
    for(int i=1;i<=n;i++)
        for(int j=m-1;j>0;j--)
            if(a[i][j+1]!=a[i][j]) r[i][j]=r[i][j+1];
    for(int i=2;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(a[i-1][j]!=a[i][j]) 
                u[i][j]=u[i-1][j]+1;
    /*
      在之前更新l、r的过程中,如果碰到相邻颜色相同的情况,
      则这两块的l、r不变,并从这两块重新开始更新,
      最后得到的l、r不一定可以从(i,j)直接延伸得到。
      因此在这里需要重新更新一下l、r
    */
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(a[i-1][j]!=a[i][j]) 
            {
                l[i][j] = max(l[i][j], l[i - 1][j]); 
                r[i][j] = min(r[i][j], r[i - 1][j]); 
            }
    int maxzheng=0;
    int maxju;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            int x=u[i][j],y=r[i][j]-l[i][j]+1;
            maxzheng=max(maxzheng,min(x,y));
            if(maxju<x*y) maxju=x*y;
        }
    printf("%d\n%d",maxzheng*maxzheng,maxju);      
}

题目6:最大子矩阵

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。

注意:选出的k个子矩阵 不能相互重叠。

解题

解题思路如下:

AC代码:

#include<iostream>
#include<cstring>
using namespace std;
const int N=110;
const int INF=1e7;
int n,m,k;
int a[N][5];
int s[N][5];
int f[N][N][15];
int main()
{
    
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            s[i][j]=s[i-1][j]+a[i][j];
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int cnt=1;cnt<=k;cnt++)
            {
                f[i][j][cnt]=-INF;
                f[i][j][cnt]=max(f[i][j-1][cnt],f[i-1][j][cnt]);
                for(int t=0;t<=i-1;t++)
                    f[i][j][cnt]=max(f[i][j][cnt],f[t][j][cnt-1]+s[i][1]-s[t][1]);
                for(int t=0;t<=j-1;t++)
                    f[i][j][cnt]=max(f[i][j][cnt],f[i][t][cnt-1]+s[j][2]-s[t][2]);
                if(i==j) 
                    for(int t=0;t<=i-1;t++)
                        f[i][j][cnt]=max(f[i][j][cnt],f[t][t][cnt-1]+s[i][1]+s[j][2]-s[t][1]-s[t][2]);
            }

    printf("%d",f[n][n][k]);
}

题目7:[USACO16OPEN]262144 P

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

Bessie likes downloading games to play on her cell phone, even though she does find the small touch screen rather cumbersome to use with her large hooves.
She is particularly intrigued by the current game she is playing. The game starts with a sequence of N positive integers (2≤N≤262,144), each in the range 1…40. In one move, Bessie can take two adjacent numbers with equal values and replace them a single number of value one greater (e.g., she might replace two adjacent 7s with an 8). The goal is to maximize the value of the largest number present in the sequence at the end of the game. Please help Bessie score as highly as possible!

解题

第一眼是一个简单的区间dp问题,思路如下:

按照这个思路,我们需要开n*n的数组,这道题里的n=262144,这个思路是行不通的。

对于区间[l,r]来说,如果能把它们全部合并,那么得到的结果一定是唯一的。

我们注意一下题目中这个奇怪的数字262144=2的18次方,也就是说,我们最多只能进行18次合并,最终得到的最大值不超过58。

由此,我们可以尝试用左端点l和当前最大值j来表示状态。

AC代码如下:

#include<iostream>
using namespace std;
const int N=270000;
int a[N];
int f[N][60];
int n;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        f[i][a[i]]=i;
    int res=0;
    for(int j=1;j<=58;j++)
        for(int l=1;l<=n;l++)
            if(f[l][j-1]&&f[f[l][j-1]+1][j-1])
            {
                f[l][j]=f[f[l][j-1]+1][j-1];
                res=max(j,res);
            }
    printf("%d",res);
}

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值