Gym-101810 G ACM International Collegiate Programming Contest, Amman Collegiate Programming Contest (2018)
阿新 • • 發佈:2020-07-11
題意
字串\(S\)的能量\(P(S)\)定義為
\[P(S)=\sum_{i=1}^{n}N_i\times V_i \]
\(N_i\)是滿足\(S_i=S_j\)的下標\(j(i<j\le n)\)的個數,\(V_i\)是字元\(S_i\)的\(ASCII\)碼。
給一個長度為\(n\)的字串\(s\)和一個整數\(k\),你可以對字串\(s\)做最多\(k\)次操作,每次操作你可以將字串\(s\)中的任意一個字元\(s_i\)變為任意一個小寫字母。你的目標是最大化字串\(s\)的能量。
分析
首先,字串\(s\)的字母的順序不會影響答案,令\(cnt[ch]\)為字元\(ch\)在字串\(s\)
從26個字母中選取一部分字母,將字串\(s\)中和這些字母相同的所有字母全部變成字母\(ch1\),選取一個字母\(ch2\),將字串\(s\)中和它相同的字母的一部分變成\(ch1\)。我們可以列舉\(ch1\)和\(ch2\),然後用個類似01揹包的dp來找選取哪些字母,將字串中所有相同的字母變成\(ch1\)。
Code
#include<algorithm> #include<iostream> #include<cstring> #include<iomanip> #include<sstream> #include<cstdio> #include<string> #include<vector> #include<bitset> #include<queue> #include<cmath> #include<stack> #include<set> #include<map> #define rep(i,x,n) for(int i=x;i<=n;i++) #define per(i,n,x) for(int i=n;i>=x;i--) #define sz(a) int(a.size()) #define rson mid+1,r,p<<1|1 #define pii pair<int,int> #define lson l,mid,p<<1 #define ll long long #define pb push_back #define mp make_pair #define se second #define fi first using namespace std; const double eps=1e-8; const int mod=1e9+7; const int N=1e5+10; const int inf=1e9; int T,n,k; int cnt[30]; ll dp[30][5010]; ll ans=0; int foo,bar; ll cal(ll x,int y){ return x*(x-1)/2*(y+'a'); } ll solve(int c,int r){ if(c==foo||c==bar) return solve(c+1,r); if(c==26){ int mn=min(cnt[bar],r); r-=mn; return cal(cnt[bar]-mn,bar)+cal(cnt[foo]+k-r,foo); } if(~dp[c][r]) return dp[c][r]; dp[c][r]=solve(c+1,r)+cal(cnt[c],c); if(r>=cnt[c]){ dp[c][r]=max(dp[c][r],solve(c+1,r-cnt[c])); } return dp[c][r]; } int main(){ //ios::sync_with_stdio(false); //freopen("in","r",stdin); scanf("%d",&T); while(T--){ scanf("%d%d",&n,&k); memset(cnt,0,sizeof(cnt)); ans=0; rep(i,1,n){ char c; cin>>c; cnt[c-'a']++; } for(int i=0;i<26;i++) ans+=cal(cnt[i],i); for(int i=0;i<26;i++){ for(int j=0;j<26;j++) if(i!=j){ memset(dp,-1,sizeof(dp)); foo=i;bar=j; ans=max(ans,solve(0,k)); } } printf("%lld\n",ans); } return 0; }