题目地址:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2547
题目意思:
给你一个字符串,长度为k的整数倍,要你分成每个长度的段
每个段内可以重新编排
连续的几个字母看作一个块
问最少有几个块
解题思路:
对于每个段来说,我们可以知道最少的块,即里面有几种字母,记为chunk[i]
我们设f[i][j]为第i段的第j位放在最末尾时的最少块数
则针对第i-1块的第l个放在末尾时来说
如果和第i的第一个相同,则可以合并一个块,则
f[i][j] = min(f[i][j],f[i-1][l]+chunk-1);
否则
f[i][j] = min(f[i][j],f[i-1][l]+chunk);
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 1010; int f[maxn][maxn]; char s[maxn]; bool vis[maxn]; int main() { int t; scanf("%d",&t); while(t--) { int k,len; scanf("%d",&k); scanf("%s",s); len = strlen(s); memset(f,0x3f3f3f3f,sizeof(f)); for(int i=1;i<=len/k;i++) { memset(vis,false,sizeof(vis)); for(int j=1;j<=k;j++) { vis[s[(i-1)*k+j-1]] = true; } int chunk = 0; for(int j='a';j<='z';j++) if(vis[j]) chunk++; if(i==1) { for(int j=1;j<=k;j++) f[1][j] = chunk; continue; } for(int j=1;j<=k;j++) { int rear = (i-1)*k+j-1; for(int l=1;l<=k;l++) { int pre = (i-2)*k+l-1; if(vis[s[pre]] && (chunk==1 || s[pre]!=s[rear])) f[i][j] = min(f[i][j],f[i-1][l]+chunk-1); else f[i][j] = min(f[i][j],f[i-1][l]+chunk); } } } int ans = 0x3f3f3f3f; for(int i=1;i<=k;i++) ans = min(ans,f[len/k][i]); printf("%d ",ans); } return 0; }