CF-div2-630-C

思路

1.与当前i相关联的有 i,i+k,i+2k n-i+1, n-i+1-k,n-i+1-2k... ,题目就是让我们把这些位置上的字符变成一样,花费的最小值
2.考虑当前这个数a[i]的局部最优解,如何花费最小,贪心的想,肯定是把与它关联的 i,i+k,i+2k n-i+1, n-i+1-k... 这些字符改成这些字符串现次数最多的字符,(改成原次数出现最多的字符,就代表“这一轮一共修改的最少”),
3.考虑贪心有没有bug,(2)中局部求最优解,但这样贪心并不一定整体就最优,想一想这样局部求最优是否有无后效性。即当前选择的结果必须不能对之前的结果状态产生影响!
考虑i从1~n 肯定会出现判重复,对重复的数相关联的数再去修改就不能达到最优了!
想到可以设置标记数组,来标记之前是否修改过,因为前面修改过的数肯定是用和它相关联的数来修改的,已经 把与他相关联的 做了修正 这次就不用考虑他了,也不用再考虑与它相关联的数了。

代码

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

int t,n,k,ans;
const int maxn = 2e5+100;
bool vis[maxn];
char s[maxn]; 
int cnt[30];

void init(){
	ans = 0;
	for(int i=1;i<=n;i++) vis[i] = false;
}

void solve(){
	cin>>n>>k;
	scanf("%s",s+1);
	init();
	for(int i=1;i<=n;i++){
		if(vis[i]) continue;
		for(int j=0;j<27;j++) cnt[j] = 0;
		int cnt1 = 0;
		int mx = 0;
		//与当前i相关联的有 i,i+k,i+2k  n-i+1, n-i+1-k... 
		//考虑从1~n判 肯定会出现判重复 设置vis数组标记用过 
		//因为前面用过的数肯定已经 把与他相关联的 做了修正 这次就不用考虑他了 
		for(int j=i;j<=n;j+=k){ //k个一组 
			if(!vis[j]){  //防止重复计算 
				vis[j] = true;
				cnt1++;
				cnt[s[j] - 'a']++;
				mx = max(cnt[s[j] - 'a'],mx);
			}else break;  
		}
		for(int j=n-i+1;j>=1;j-=k){ //i对应的回文(另一端) k个一组 
			if(!vis[j]){
				vis[j] = true;
				cnt1++;
				cnt[s[j] - 'a']++;
				mx = max(cnt[s[j] - 'a'],mx);
			}else break;
		}
		ans += cnt1 - mx;
	}
	cout<<ans<<endl;
}

int main(){
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}
原文地址:https://www.cnblogs.com/fisherss/p/12610856.html