【题意】给定大写字母字符串,交换相邻字符代价为1,求最小代价使得字符串不含"VK"子串。n<=75。
【算法】动态规划
【题解】关键在于表示状态,我们将确定下来的前若干个固定作为状态,后面新加的字符不会进入固定的前若干个。(为了方便,非'V''K'的字符皆为‘X')
由于相同字符显然不可能跨越,那么前若干个的有效信息只有:它是由前v个’V',前k个‘K',前x个’X'组成的,最后一个字符是否’V',即f[v][k][x][0/1]。
转移时枚举新加入的字符,问题在于统计新加入字符前有多少未固定字符,就是加入这个字符的代价。
假设0是'V',1是‘K',2是’X',很容易预处理p[i][0]表示第i个'V'的位置,c[i][0]表示前i个位置‘V'的数量(1和2同理),然后就很容易计算了。
如果推不清楚可以参考代码,很好理解。
复杂度O(n^3)。
#include<cstdio> #include<cstring> int n,p[110][3],c[110][3],f[110][110][110][2],V,K,X; char s[110]; int min(int a,int b){return a<b?a:b;} int r(int o,int v,int k,int x){return o-min(c[o][0],v)-min(c[o][1],k)-min(c[o][2],x);} int main(){ scanf("%d%s",&n,s+1); for(int i=1;i<=n;i++){ for(int j=0;j<3;j++)c[i][j]=c[i-1][j]; if(s[i]=='V')V++,p[V][0]=i,c[i][0]++;else if(s[i]=='K')K++,p[K][1]=i,c[i][1]++;else X++,p[X][2]=i,c[i][2]++; } memset(f,0x3f,sizeof(f)); f[0][0][0][0]=0; for(int i=0;i<=V;i++) for(int j=0;j<=K;j++) for(int k=0;k<=X;k++){ int Q=min(f[i][j][k][0],f[i][j][k][1]); f[i+1][j][k][1]=min(f[i+1][j][k][1],Q+r(p[i+1][0],i+1,j,k)); f[i][j+1][k][0]=min(f[i][j+1][k][0],f[i][j][k][0]+r(p[j+1][1],i,j+1,k)); f[i][j][k+1][0]=min(f[i][j][k+1][0],Q+r(p[k+1][2],i,j,k+1)); } printf("%d",min(f[V][K][X][0],f[V][K][X][1])); return 0; }