一道不错的dp题

来源:互联网 发布:淘宝入住条件与费用 编辑:程序博客网 时间:2024/05/21 09:33

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;}



0 0