一道不错的dp题

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值