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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章