CodeForces 1238-E Keyboard Purchase(状压DP)

题目:

传送门

思路:

先预处理出所有的字符的贡献对,例如 abcd,则贡献对为(a,b),(b,c),(c,d) 均为一个.
如果在新建盘中一个字符 位置 pos , 则与其有关的贡献对中,编号比他大的对中就要减去它,即为 - pos, 反之就是加上它,即为 +pos
可以看出我们可以把 单个字符的贡献单独放出来算,所以我们考虑直接枚举顺序,每次维护最小值.
而状压dp转移的过程中恰好可以将顺序一一枚举

Ac_Code:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define fir first
#define sec second
using namespace std;
 
const int maxn = (1<<20)+7;
const int INF = 1e9+7;
 
int n,m;
string s;
long long dp[maxn];
long long val[maxn][25];
int vis[maxn];
int len[maxn];
int bit[25];
int mn[30][30];
int all[30];
 
int main() {
    cin>>n>>m>>s;
    bit[0] = 1;
    for(int i=1;i<=m;i++) bit[i] = bit[i-1]*2;
    for(int i=1;i<n;i++) {
        int d = s[i]-'a';
        int dd = s[i-1]-'a';
        if(d == dd) continue;
        mn[dd][d]++;
        mn[d][dd]++;
        all[d]++;
        all[dd]++;
    }
    int up = (1<<m);
    for(int i=0;i<up;i++) dp[i] = INF,dp[0] = 0;
    for(int i=0;i<up;i++) {
        for(int j=0;j<m;j++) {
            if(bit[j]&i) continue;
            int res = (i|bit[j]);
            dp[res] = min(dp[res],dp[i] + (2*val[i][j]-all[j])*(len[i]+1));
            if(vis[res] == 0) {
                for(int l=0;l<m;l++) {
                    val[res][l] = val[i][l] + mn[j][l];
                }
                len[res] = len[i]+1;
            }
            vis[res] = 1;
        }
    }
    printf("%lld\n", dp[up-1]);
    return 0;
}

你可能感兴趣的:(CF200题计划,DP)