训练集---dp崭露头角篇

前言

dp较难的一系列题目,两天只做出了五道,日后慢慢补吧。。

Zuma

Genos recently installed the game Zuma on his phone. In Zuma there exists a line of n gemstones, the i-th of which has color ci. The goal of the game is to destroy all the gemstones in the line as quickly as possible.

In one second, Genos is able to choose exactly one continuous substring of colored gemstones that is a palindrome and remove it from the line. After the substring is removed, the remaining gemstones shift to form a solid line again. What is the minimum number of seconds needed to destroy the entire line?

Let us remind, that the string (or substring) is called palindrome, if it reads same backwards or forward. In our case this means the color of the first gemstone is equal to the color of the last one, the color of the second gemstone is equal to the color of the next to last and so on.
第一题,看到后一脸那啥,完全没有思路。题意是一串数字,每次能消去一个回文字串,问最小消去次数。
去问度娘学了些区间类的dp才勉强打出来。

#include<bits/stdc++.h>
#define N 510
using namespace std;
int dp[N][N],a[N];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=0; i<n; i++) {
        scanf("%d",&a[i]);
    }
    for(int i=0; i<n; i++) {
        for(int j=0; j<n-i; j++) {
        dp[j][j+i]=min(i+1,1+dp[j+1][j+i]);
        if(a[j]==a[j+1]) dp[j][j+i]=min(dp[j][j+i],1+dp[j+2][j+i]);
        for(int k=j+2; k<=j+i; k++) 
        if(a[j]==a[k]) 
            dp[j][j+i]=min(dp[j][j+i],dp[j+1][k-1]+dp[k+1][j+i]);                       
        }
      }
    cout<<dp[0][n-1];
}

Hard Process

You are given an array a with n elements. Each element of a is either 0 or 1.

Let’s denote the length of the longest subsegment of consecutive elements in a, consisting of only numbers one, as f(a). You can change no more than k zeroes to ones to maximize f(a).
题目短,写起来还是蛮烦的。就是一串01数字序列,你可以换其中k个0到1,问可以搞出的最大连续1序列长度。
思路就是找到每个1开头,之后向后扫。如果这个1和前面那个1相距小于k(表示之间0个数小于k)那么我们可以调用上一次的结果(虽然我也不知道这是不是dp,但发现和暴力的不同也就在这了)ps:注意第一次从第一个数开始,不管第一个数是0还是1,之后则只找1.

#include<bits/stdc++.h>
#define N 300005
using namespace std;
int main() {
    int n,k,i,j,dp[N]= {0},m,x,p=0,maxn=0,num,cnt,aa,y=0;
    bool a[N],flag=0;
//  freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&k);
    for(i=1; i<=n; i++)
        {scanf("%d",&aa);
         a[i]=(bool)aa;
         if(aa==1)flag=1;
        }
    if(flag==0)
    {cout<<k<<endl;
     for(i=1;i<=n;i++)
     if(i<=k)printf("1 ");
     else printf("0 ");
     return 0;
    }
    for(i=1; i<=n; i++) {
        if(a[i]==0)continue;
        else {
            cnt=0;
            for(j=i+1; j<=n; j++) {
                if(a[j]==0)cnt++;
                if(cnt>k)
                    break;
            }
            if(cnt<=k)y=k-cnt;
            dp[i]=j-i;
            maxn=dp[i]+y;
            num=max(1,i-y);
            p=j;
            m=i;
            x=i;
            break;
        }
    }
    for(i=x+1; i<=n; i++) {
        if(a[i]==0)continue;

        else {
            if(i-m-1<k) {
                cnt=k-(i-m-1);
                for(j=p;j<=n; j++) {
                    if(a[j]==0)cnt++;
                    if(cnt>k)break;
                }
                dp[i]=j-i;
                 y=0;
                 if(cnt<=k)y=k-cnt;
                if(dp[i]+y>maxn) {
                    maxn=dp[i]+y;
                    num=i-y;

                }
                p=j;
                m=i;
            }
            else if(i-m-1>=k) {
                cnt=0;
                for(j=i+1; j<=n; j++) {
                    if(a[j]==0)cnt++;
                    if(cnt>k)break;
                }
                y=0;
                if(cnt<=k)y=k-cnt;
                dp[i]=j-i;
                if(dp[i]+y>maxn) {
                    maxn=dp[i]+y;
                    num=i-y;

                }
                p=j;
                m=i;
            }

        }
    }
    //cout<<num<<endl;
    cout<<min(n,maxn)<<endl;
    cnt=0;bool flg=0;
  for(i=1;i<=n;i++)
  {if(i>=num)flg=1;
  if(flg==1&&a[i]==0&&cnt<k)
  {printf("1 ");cnt++;
  }
  else 
  printf("%d ",a[i]); 
  if(cnt==k)flg==0;
  }
}

Arthur and Table

Arthur has bought a beautiful big table into his new flat. When he came home, Arthur noticed that the new table is unstable.

In total the table Arthur bought has n legs, the length of the i-th leg is li.

Arthur decided to make the table stable and remove some legs. For each of them Arthur determined number di — the amount of energy that he spends to remove the i-th leg.

A table with k legs is assumed to be stable if there are more than half legs of the maximum length. For example, to make a table with 5 legs stable, you need to make sure it has at least three (out of these five) legs of the maximum length. Also, a table with one leg is always stable and a table with two legs is stable if and only if they have the same lengths.

Your task is to help Arthur and count the minimum number of energy units Arthur should spend on making the table stable.
亚瑟王和圆桌。。。不过这里是要亚瑟王去锯一个圆桌的桌腿(吐槽,真的能有十万条腿的桌子吗。。)
要求:锯到最大长度的腿的数量大于(不能等于,如4条桌要有3条最长)。思路就是从大到小往下锯。要注意数据要用前缀和之类的优化,不然会tle。这里也把tle的和ac的一起挂上来了。

tle

#include<bits/stdc++.h>
#define N 100005
using namespace std;
struct gg{
    int len,eng;
}foot[N],feet[N];
bool cmp(gg a,gg b)
{return a.len>b.len;
}
bool cmpp(gg a,gg b)
{return a.eng<b.eng;
}
int dp[N]={0};
int main() {
    int n,i,ans=99999999,j,p,x;
    //freopen("in.txt","r",stdin);
    cin>>n;
    for(i=1;i<=n;i++)
      {scanf("%d",&foot[i].len);
       feet[i].len=foot[i].len;
      }
    for(i=1;i<=n;i++)
      {scanf("%d",&foot[i].eng);
       feet[i].eng=foot[i].eng;
      }
    sort(foot+1,foot+n+1,cmp);
    sort(feet+1,feet+n+1,cmpp);
    foot[n+1].len=-1;
    dp[1]=foot[1].eng;
    for(i=2;i<=n;i++)
       dp[i]=dp[i-1]+foot[i].eng;
    for(i=1;i<=n;i++)
      {int cnt=1;int sum=0;p=0;
        for(j=i+1;j<=n+1;j++)
         {if(foot[j].len==foot[i].len)
           cnt++;
          else break; 
         }
         j--;p=n-(i-1);
         sum+=dp[i-1];
         if(j-i+1>p/2){ans=min(sum,ans);
         }
         else{//cout<<i<<" "<<j<<" "<<p<<endl;
            for(x=1;x<=n;x++)
            {if(feet[x].len<foot[i].len)
              {sum+=feet[x].eng;p--;
               }
              if(j-i+1>p/2)break; 
             }
         ans=min(sum,ans);//cout<<foot[i].len<<" "<<sum<<" "<<ans<<endl;
           }
         i=j;       
      }
     cout<<ans;
}

ac

#include<bits/stdc++.h>
#define N 100005
using namespace std;
struct gg{
    int len,eng;
}foot[N];
bool cmp(gg a,gg b)
{return a.len>b.len;
}
int dp[N]={0};
int dp1[N][201]={0};
int main() {
    int n,i,ans=99999999,j,p,x,wh,cnt;
    //freopen("in.txt","r",stdin);
    cin>>n;
    for(i=1;i<=n;i++)
      scanf("%d",&foot[i].len);
    for(i=1;i<=n;i++)
      scanf("%d",&foot[i].eng);
    sort(foot+1,foot+n+1,cmp);
    bool b=0;
    for(i=2;i<=n;i++)
    {//cout<<foot[i].len;
      if(foot[i].len!=foot[i-1].len&&b==0)
        {b=1;wh=i-1;
        }
     if(b==1)
        {dp1[wh][foot[i].eng]++;//cout<<wh<<" "<<foot[i].eng<<" "<<dp1[wh][foot[i].eng]<<endl;
        }
    }
    for(i=wh+1;i<=n;i++)
        {if(foot[i].len!=foot[i+1].len)
          {for(j=1;j<=200;j++)
           {dp1[i][j]=dp1[wh][j];//if(dp1[i][j])cout<<i<<" "<<j<<" "<<dp1[i][j]<<endl;
           }
          for(j=wh+1;j<=i;j++)
           {dp1[i][foot[j].eng]--;
            //cout<<i<<" "<<foot[j].eng<<" "<<dp1[i][foot[j].eng]<<endl;
           }
           wh=i;continue;
          }      
        }
    foot[n+1].len=-1;
    dp[1]=foot[1].eng;
    for(i=2;i<=n;i++)
       dp[i]=dp[i-1]+foot[i].eng;
    for(i=1;i<=n;i++)
      {int cnt=1;int sum=0;p=0;
        for(j=i+1;j<=n+1;j++)
         {if(foot[j].len!=foot[i].len)
           break;
         }
         j--;p=n-(i-1);
         sum+=dp[i-1];
         if(j-i+1>p/2)ans=min(sum,ans);
         else{//cout<<i<<" "<<j<<" "<<p<<endl;
            for(x=1;x<=200;x++)
            {if(dp1[j][x])
              {sum+=x*dp1[j][x];p-=dp1[j][x];
               }
             if(j-i+1>p/2)
              {if(2*(j-i+1)>p+1)sum-=x*(2*j-2*i+2-p-1);break;
              }
             }
         ans=min(sum,ans);
           }
         i=j;       
      }
     printf("%d",ans);
}

Riding in a Lift

Imagine that you are in a building that has exactly n floors. You can move between the floors in a lift. Let’s number the floors from bottom to top with integers from 1 to n. Now you’re on the floor number a. You are very bored, so you want to take the lift. Floor number b has a secret lab, the entry is forbidden. However, you already are in the mood and decide to make k consecutive trips in the lift.

Let us suppose that at the moment you are on the floor number x (initially, you were on floor a). For another trip between floors you choose some floor with number y (y ≠ x) and the lift travels to this floor. As you cannot visit floor b with the secret lab, you decided that the distance from the current floor x to the chosen y must be strictly less than the distance from the current floor x to floor b with the secret lab. Formally, it means that the following inequation must fulfill: |x - y| < |x - b|. After the lift successfully transports you to floor y, you write down number y in your notepad.

Your task is to find the number of distinct number sequences that you could have written in the notebook as the result of k trips in the lift. As the sought number of trips can be rather large, find the remainder after dividing the number by 1000000007 (109 + 7).
乘电梯,每次向上/向下移动距离不能超过第y层到现在所处层的距离(同时也是永远不能到y),
问走法数。
思路:用前缀数组来记录在前一趟中到达这个点以前的方案总数有几种。这样对于任意一个成段的满足的线段都可以求出在这个区间内的方案总数了,将转移状态的复杂度降至O(1)。此时可以分类讨论,如果当前楼层在b的左边可以求出之前转移的一段线段,右边又是一种。

#include<bits/stdc++.h>
#define N 5010
using namespace std;
int n,k,a,b,dp[N][N],foot[N]={0},mod=1e9+7,ret=0,j;
int main() {
    int i;
    cin>>n>>a>>b>>k;
    memset(dp,0,sizeof(dp));
    dp[0][a]=1;
    //if(a<b)
    for(i=1;i<=k;i++)
    {foot[0]=0;
    for(j=1;j<=n;j++)
     {foot[j]=foot[j-1]+dp[i-1][j];
     foot[j]%=mod;
     //cout<<foot[j]<<endl;
     } 
     for(j=1;j<=n;j++)
      {if(j==b)continue;
      if(j<b)
      dp[i][j]=(foot[j+(b-j-1)/2]+mod)%mod;
      else dp[i][j]=(foot[n]-foot[j-(j-b+1)/2]+mod)%mod;
      dp[i][j]-=dp[i-1][j];
      dp[i][j]+=mod;
      dp[i][j]%=mod;
      }
    }
    //if(a>b)
    for(i=1;i<=n;i++)
    {ret+=dp[k][i];
     ret%=mod;
     //cout<<ret<<endl;
    }
    printf("%d",ret);
}

最大子矩阵

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.
在矩阵中挑出一个小矩阵,使得小矩阵内所有数和最大。
先算出一列列的前缀和,之后再dp列。

#include<cstdio>
#include<iostream>
#define N 130
using namespace std;
int main() {
    int n,i,x,j,dp[N][N],maxn=-99999;
    //freopen("in.txt","r",stdin);
    cin>>n;
    for(i=1;i<=n;i++)
     for(j=1;j<=n;j++)
      {scanf("%d",&dp[i][j]);
      maxn=max(maxn,dp[i][j]);
       dp[i][j]+=dp[i-1][j];       
      }
    for(i=1;i<=n;i++)
       for(j=i;j<=n;j++)
       {int ans=0;
         for(x=1;x<=n;x++)
         {ans+=dp[j][x]-dp[i-1][x];
          maxn=max(ans,maxn);
          ans=max(ans,0);
         }
       }
    cout<<maxn;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值