题目链接:
http://community.topcoder.com/stat?c=problem_statement&pm=12728&rd=15701
这是个看了题解才会做的题目。
这个题目需要总结出来一些规律:
1)可以用M将整个串分成N/M个组,因为大翻转都是按组为单位的,所以在组的翻转策略被定下来时,内部的翻转都是唯一的,并且可以预计在组的翻转与否确定的情况下需要的组内翻转次数。也就是0或者1的个数。
2)对于以组为单位的翻转,假设翻转的决定是固定的,可以记翻转为1,不翻转为0,表示为一个0-1串。对于这个0-1串,他的最小翻转次数是有规律的:
- 如果这个翻转决定为111...1,则翻转次数为1.
- 如果其中有0有1,则翻转次数为01间隔的个数。如1010,翻转次数为3.
根据这个结论,就能推导出递推式了:
f (t , p, q) :假定已经确定了[t,n)组的翻转,其中t处的翻转决定为p(0或1),则前t组的最小翻转次数是多少。
q用来标记当前是否是全为111的情况。
子问题就是当前选1或者0时,当前需要花费的翻转开销+f(t-1,p',q')。
其中当前花费的翻转开销分为两部分:
1. 当前是否与之前不一样01间隔。
2. 当前情况下,内部的翻转数。
代码如下:
vector <int> A; vector <int> c[2]; int dp[2501][2][2]; int Solve(int t,int p,int q){ if (dp[t][p][q]!=-1){return dp[t][p][q];} if (t==0){ if (p==1 && q==1){ //non-flip at all(q==1) and infact previous choice is 1 return 1; } return 0; } int res=numeric_limits<int>::max(); //0: flip cur group verse vice for (int i=0;i<2;i++){ int change=q; //has a change happened? int curChange=0; //if current change from previous if (t!=m){ curChange=i^p; if (change==1){ change=!(p ^ i); } } res=min(res,curChange+c[i][t-1]+Solve(t-1,i,change)); } dp[t][p][q]=res; return res; } int getmin(vector <string> S, int M) { memset(dp,-1,sizeof(dp)); //devide S into |S|/M groups for (int i=0;i<S.size();i++) for (int j=0;j<S[i].length();j++){ A.push_back(S[i][j]-'0'); } n=A.size(); m=n/M; c[0].resize(m,0); c[1].resize(m,0); //statistic the op of flipping or non-flipping bits of every group. for (int i=0;i<m;i++) for (int j=0;j<M;j++){ int p=i*M+j; if (A[p]==0){c[0][i]++;} else if (A[p]==1){c[1][i]++;} } //first m groups, last choice is 0, allflip flag is 1 return Solve(m,0,1); }