第七天——动规

 今天是集训的第七天,也是集训的倒数第二天。今天又有一个新老师来上课是个女老师,传说是高三的一位大佬百忙中来给我们上课。
今天的题出的很有良心。
第一题:求一个序列中两个不重叠子串的最大和。
这题上课讲过,老师讲的我全打了,但我突然发现我不会求最大子串。还吧我无奈之下我只能用我自认为对的做法。当然我还是知道是错的。
对于30% 的数据,有2 ≤ N ≤ 80 枚举两个区间的左端点、右端点,利用前缀和O(1) 统计答案。 时间复杂度O(n4) 对于100% 的数据,有2 ≤ N ≤ 100000 由于是两段, 因此必然会有一个分界点, 可以从这里把序列切开。 那么,我们只要O(N) 枚举分界点i,如果能快速求出[1,i] 和[i + 1,N] 中的最大子段和,那么本题就能成功解决。 f [i] 表示[1,i] 中的最大子段和,g[i] 表示[i + 1,N] 中的最大子段 和,预处理f [i],g[i],即正着一遍、反着一遍做最大子段和即可。 时间复杂度O(N)。
#include<bits/stdc++.h>
#define N 100010
intn,a[N],f[N],g[N],ans=-1<<20;
#define cut(a) (a>0?a:0)
#define cmax(a,b) (a<b?a=b:1)
intmain(){
freopen("sequence.in 

","r",stdin);
freopen("sequence.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",a+i),f[i]=cut(f[i-1])+a[i];f[0]=f[n+1]=g[0]=g[n+1]=ans;
for(inti=n;i;i--)g[i]=cut(g[i+1])+a[i];
for(inti=n;i;i--)cmax(g[i],g[i+1]);
for(inti=1;i<=n;i++)cmax(f[i],f[i-1]),cmax(ans,f[i]+g[i+1]);
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
}
第二题:给一个n个元素的数组,其中0可以变成任意整数,求最长上升子序列长度。这题老师上课讲过,不该零的。但我还是因为不会求最长上升子序列导致我连这题讲过的50分也没拿到。再加个改零我更不会了。给一个n个元素的数组,其中0可以变成任意整数,求最长上升子序 列长度。 1 ≤ n ≤ 1000000 0可以转化成任意整数,包括负数,显然求LIS时尽量把0都放进去 必定是正确的。因为0放不进去的原因无非就是非严格递增。 如何保证严格递增? 我们可以将每个数a[i]减去i前面0的个数,再做LIS,就能保证结果 是严格递增的。给一个n个元素的数组,其中0可以变成任意整数,求最长上升子序 列长度。 1 ≤ n ≤ 1000000 0可以转化成任意整数,包括负数,显然求LIS时尽量把0都放进去 必定是正确的。因为0放不进去的原因无非就是非严格递增。 如何保证严格递增? 我们可以将每个数a[i]减去i前面0的个数,再做LIS,就能保证结果 是严格递增的。 因此我们可以把0剔除,对剩下的数列做LIS,统计结果的时候再算 上0的个数即可。 时间复杂度O(nlogn)。
#include <bits/stdc++.h>
using namespace std;
constint N=1e5+50;
constint M=1e9+7;
intt,n,a[N],dp[N],num[N];
interfen(intl,intr,int u){
while(l<=r){
int mid=(l+r)>>1;
    if(dp[mid]<a[u]&&dp[mid+1]>=a[u])
        return mid;
    else if(dp[mid]<a[u])
        l=mid+1;
    else
        r=mid-1;
}
    return 0;
}
intmain()
{
int cnt0=0;
freopen("square.in 

","r",stdin);
freopen("square.out","w",stdout); 
scanf("%d",&n);
for(inti=1;i<=n;i++)
dp[i]=1000000+10,num[i]=0;
dp[0]=0,num[0]=0;
inti,j,cnt=0;
for(i = 1;i <= n;i ++){
scanf("%d",&a[i]);
        if(a[i]==0)
cnt++,cnt0++;
        if(a[i]!=0)
num[i]=cnt;
    }
    for(i=1,cnt=0;i<=n;i++){
        if(a[i]!=0){
        a[++cnt]=a[i]-num[i];
        }
    }

intlen=0;
dp[1]=min(dp[1],a[1]);
len++;
    if(cnt==0)
len=0;
for(i = 2;i <= cnt;i ++){
        if(a[i]>dp[len])
            j=++len;
        else j=erfen(1,len,i)+1;
dp[j]=a[i];
    }

printf("%d",len+cnt0);
fclose(stdin);
fclose(stdout);
    return 0;
}
第三题:N个不同正整数组成的序列,每次可从左边或右边去掉连续的i个 数,重复操作直到所有的数都被删除为止。删除从i 位置到k 位置上的 所有数的操作价值为— xi ×xk —(k −i + 1),求操作的最大价值和。这题和老师上课讲的乘法游戏是差不多的,不过题面改了,但我还是不会。N个不同正整数组成的序列,每次可从左边或右边去掉连续的i个 数,重复操作直到所有的数都被删除为止。删除从i 位置到k 位置上的 所有数的操作价值为— xi ×xk —´(k −i + 1),求操作的最大价值和。 3 ≤ N ≤ 100 区间DP,设f [i][j]表示区间[i,j]的最大操作价值。 枚举k表示接下来删到哪位即可。 时间复杂度O(N3)
#include<bits/stdc++.h>
using namespace std;
constintMaxN=101;
int n;
int a[MaxN],f[MaxN][MaxN],d[MaxN][MaxN];
void init()
{inti,j;
scanf("%d",&n);
 for (i=1;i<=n;i++)
scanf("%d",&a[i]);//第i个数的值,也是删除第i个数得到的操作价值 
 for (i=1;i<=n;i++)
   {d[i][i]=a[i];
    for (j=i+1;j<=n;j++)
      d[i][j]=abs(a[i]-a[j])*(j-i+1);//d[i][j]是一次性删除第i到第j个数得到的操作价值 
   }  
}
void dp()
{intlen,i,j,k;
memset(f,0,sizeof(f));
 for (i=1;i<=n;i++)
   f[i][i]=a[i];
 for (len=1;len<=n-1;len++)
   for (i=1;i<=n-len;i++)
     {j=i+len;
      f[i][j]=d[i][j]; 
      for (k=i;k<=j-1;k++)
        f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);
     }
printf("%d\n",f[1][n]);
}

intmain()
{freopen("remove.in 

","r",stdin);
freopen("remove.out","w",stdout);
init();
dp();
fclose(stdin);
fclose(stdout);
 return 0;
}
第四题:有一个长度为N 的数列A1•••N,可以从里面按顺序选若干个数,并 且每M 个数必须选一个,记为B1•••T,最大化 T Pi =1 ki−1Bi。这题爆力搜索有30分。对于60% 的数据,有1 ≤ M ≤ N ≤ 1000。 f [i][j] 表示[1,i] 中取了j 个数,并且强制i 要取。 f [i][j] = max{f [x][j −1] + kj−1 ×a[i]} max(j −1,i −M) ≤ x <i 时间复杂度O(N^3)对于60% 的数据,有1 ≤ M ≤ N ≤ 1000。 f [i][j] 表示[1,i] 中取了j 个数,并且强制i 要取。 f [i][j] = max{f [x][j −1] + kj−1 ×a[i]} max(j −1,i −M) ≤ x <i 时间复杂度O(N^3) 注意j只和j −1 有关,因此可以把第二维滚动掉。 运用单调队列可使得x 不必枚举。 由于统计答案时必须要查看所有的f [i][j],因此该算法复杂度下界 是O(N^2)。
(Spana) 动
#include<bits/stdc++.h>
intn,m,q[100010],a[100010],l,r;
double k,f[100010];
intmain(){
freopen("explore.in 

","r",stdin);
freopen("explore.out","w",stdout);
scanf("%d%d%lf",&n,&m,&k);
for(inti=1;i<=n;i++)scanf("%d",a+i);
for(inti=n;i;q[++r]=i--){
if(l<=r&&q[l]>m+i)l++;
f[i]=f[q[l]]*k+a[i];
while(l<=r&&f[q[r]]<=f[i])r--;
}
if(l<=r&&q[l]>m)l++;
printf("%.2lf\n",f[q[l]]);
fclose(stdin);
fclose(stdout);
}OK明天是最后一天,加油。

猜你喜欢

转载自blog.csdn.net/wangzhuojia/article/details/79324994
今日推荐