http://codeforces.com/contest/18/problem/E
状态很容易设计,但如果直接求解的话复杂度很高,需要技巧优化决策步骤。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN(50010);
template<typename T>
bool checkmin(T &a, const T &b){
return b < a? (a = b, true): false;
}
template<typename T>
bool checkmax(T &a, const T &b){
return b > a? (a = b, true): false;
}
inline int lowb(int i){return i&-i;}
int gcd(int a, int b){
while(b){
int t = a%b;
a = b;
b = t;
}
return a;
}
struct AR{
int v, s;
friend bool operator <(const AR &a, const AR &b){
return a.v == b.v? a.s < b.s: a.v < b.v;
}
}ar[651];
bool co[651][651]; //co[i][j]表示编号为i的决策是否与编号为j的决策冲突,i与j为相邻两行决策
int op[651][2], mp[26][26], pre[501][651], dp[501][651];
char ma[501][501];
int main(){
int cnt = 0;
for(int i = 0; i < 26; ++i)
for(int j = 0; j < 26; ++j)
if(i != j){
mp[i][j] = ++cnt;
op[cnt][0] = i;
op[cnt][1] = j;
}
for(int i = 0; i < 26; ++i)
for(int j = 0; j < 26; ++j)
if(i != j){
for(int k = 0; k < 26; ++k){
if(i != k) co[mp[i][j]][mp[i][k]] = true;
if(j != k) co[mp[i][j]][mp[k][j]] = true;
}
}
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%s", ma[i]);
for(int i = 1; i <= n; ++i){
int tn = 0;
for(int j = (i == 1? 0: 1); j <= cnt; ++j){
ar[tn].v = dp[i-1][j];
ar[tn++].s = j;
}
sort(ar, ar+tn);
for(int j = 1; j <= cnt; ++j) dp[i][j] = m;
for(int j = 0; j < m; ++j){ //利用小字符集优化转移费用计算
int t = ma[i][j]-'a';
for(int k = 0; k < 26; ++k)
if(t != k){
if(j&1)
--dp[i][mp[k][t]];
else
--dp[i][mp[t][k]];
}
}
for(int j = 1; j <= cnt; ++j)
for(int k = 0; k < tn; ++k) //可以证明k<=50时一定会break
if(!co[j][ar[k].s]){
dp[i][j] += ar[k].v;
pre[i][j] = ar[k].s;
break;
}
}
int ans = n*m+1, ts;
for(int i = 1; i <= cnt; ++i)
if(checkmin(ans, dp[n][i]))
ts = i;
for(int i = n; i >= 1; --i){
for(int j = 0; j < m; ++j) ma[i][j] = op[ts][j&1]+'a';
ts = pre[i][ts];
}
printf("%d\n", ans);
for(int i = 1; i <= n; ++i) printf("%s\n", ma[i]);
return 0;
}