Smooth Array

Smooth Array
题意:给n个数,每次操作可以修改数组中的一个数使之为任意数,求最小修改次数,使得修改完的数组每k个连续的数之和为s。
思路:因为每k个连续的数之和为s则,a[i]=a[i+k],即a数组最终为一个k周期的循环,然后问题化简为k组背包问题,即求出dp[k][s]即可,dp[i][j]就是前i组数和为j的最大不需要操作数,因为分组背包是求最大值的,所以需要进行转换求最大不需要操作数,最后答案为n-dp[k][s]。
分组背包一般为n * n * n的复杂度,明显需要优化。n的立方复杂度算法为

for(i=1;i<=k;i++){
 for(j=1;j<=s;j++){
  for(x=0;x<=s;x++){
   dp[i][j]=max(dp[i][j],dp[i-1][j-x]+val[i][x]);
  }
 }
}

显而易见,如果x是第k组中没有出现过的数,那么val则为0,那么就可以只枚举出现过的数,然后剩余的进行前缀和最大值处理即可。
而且这个题k能不能整除n算法都适用,只不过需要注意一下循环第三层x的最大值为(n-1)/k+1即可(不是n/k)。
复杂度为ks(n/k),即为n*s。
注意一下j维需要从0开始,因为可以把a数组可以为0,然后再注意初始化dp[0][i]为-INF,即不可能即可。

#include<iostream>
#include<cstdio>
using namespace std;
const int MAX_N=5010;
const int INF=0x3f3f3f3f;
int a[MAX_N];
int val[MAX_N][MAX_N];
int dp[MAX_N][MAX_N];
int maxl[MAX_N];
int main(void){
 int n,k,s,i,j,x;
 scanf("%d%d%d",&n,&k,&s);
 for(i=1;i<=n;i++){
  scanf("%d",&a[i]);
  val[(i-1)%k+1][a[i]]++;
 }
 for(j=1;j<=s;j++)
 dp[0][j]=-INF;
 for(i=1;i<=k;i++){
  for(j=0;j<=s;j++){
   for(x=0;x<(n-1)/k+1;x++){
    //cout<<x*k+i<<" "<<a[x*k+i]<<" "<<i<<" "<<val[i][a[x*k+i]]<<"\n";
    if(x*k+i<=n&&j>=a[x*k+i])
    dp[i][j]=max(dp[i][j],dp[i-1][j-a[x*k+i]]+val[i][a[x*k+i]]);
   }
   //cout<<i<<" "<<j<<" "<<dp[i][j]<<"\n";
   dp[i][j]=max(dp[i][j],maxl[j]); 
  }
  for(j=0;j<=s;j++){
   maxl[j]=max(maxl[j-1],dp[i][j]);
  }
 }
 int ans=n-dp[k][s];
 cout<<ans<<"\n";
 return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值