ICPC North Central NA Contest 2017 - D. Smooth Array 分組揹包+前綴最大值轉移

題目鏈接:https://nanti.jisuanke.com/t/43371
題目大意:有n個數。給你一個k和s。每個數字可以改變成0-s。讓你以相鄰的k個爲一組[a[1]…a[k]], [a[2]…a[k+1]]…。問使每一組的和爲s。至少需要改變多少個數。
在這裏插入圖片描述
根據題目的條件這個數組最後的樣子一定是以k爲循環節。a[1]=a[k+1],a[2]=a[k+2]…

那麼就是有k組數。我們把每組數出現的次數計爲:size。那麼就可以變成一個容量爲S的分組揹包。出現的數組體積爲數字。貢獻爲size(如果把這組都變成這個數字,那麼這組可以不用改變的數字個數)。
最後n-f[n][s]就是答案。

x:f[i][s]=max(f[i][sx],f[i][j]+size[i][x])轉移方程:如果x在這組出現過:f[i][s]=max(f[i][s-x], f[i][j]+size[i][x])x:f[i][s]=max(f[i1][sx],f[i][j])如果x在這組沒有出現過:f[i][s]=max(f[i-1][s-x], f[i][j])

如果我們枚舉X。複雜度就是O(KSS)O(K*S*S)是不可以的。

x:f[i][s]=max(f[i1][sx],f[i][j])f[i1][s]如果x在這組沒有出現過:f[i][s]=max(f[i-1][s-x], f[i][j])就是f[i-1][s]的前綴最大值。我們處理一下,\\就可以只枚舉出現過的數字
nkn/k:O(KS(N/K))=O(SN)長度爲n有k組。每組有n/k個。時間複雜度:O(K*S*(N/K))=O(S*N)

#include<bits/stdc++.h>
#define LL long long
using namespace std;

int a[5005];
int siz[5005][5005];
vector<int> v[5005];
int f[5005][5005];
int g[5005][5005];
int main(){

    int n, k, s;scanf("%d%d%d", &n, &k, &s);
    for(int i=0; i<n; i++){
        scanf("%d", &a[i]);
        if(siz[i%k+1][a[i]]==0){
            v[i%k+1].push_back(a[i]);
        }
        siz[i%k+1][a[i]]++;
    }
    for(int i=1; i<=s; i++){
        f[0][i]=-1<<30;
    }

    f[0][0]=0;
    for(int i=1; i<=k; i++){

        for(int j=0; j<=s; j++){
            g[i][j]=g[i-1][j];
        }
        for(int j=0; j<=s; j++){
            g[i][j]=max(j?g[i][j-1]:0, f[i-1][j]);
        }

        for(int j=0; j<=s; j++){

            //f[i][j]=f[i-1][j]+siz[i][0];
            for(int k=0; k<v[i].size(); k++){
                if(j>=v[i][k]){
                    f[i][j]=max(f[i][j], f[i-1][j-v[i][k]]+siz[i][v[i][k]]);
                }
                else{
                    f[i][j]=max(f[i][j], f[i-1][j]);
                }
            }
        }

        for(int j=0; j<=s; j++){
            f[i][j]=max(f[i][j], g[i][j]);
        }
    }
    cout<<n-f[k][s]<<endl;

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章