Educational Codeforces Round 74E 状态压缩dp

Educational Codeforces Round 74E 状态压缩dp

https://codeforces.com/problemset/problem/1238/E
题意:
一个字符串,你来安排一个字符排列,计算使得\(\sum_{i=1}^{n}|pos_{s_{i-1}}-pos_{s_{i}}|\)最小。(例子里面很清楚

思路:
状态压缩dp,dp[111111]表示都放进去的最小值就好了,但是这个最小是什么最小就很微妙了,比如说如果我维护目前开销最小肯定是不行的,因为状态放进去的顺序是影响答案的,我们也没法转移,也就是无法取消后效性。这里的最小考虑最总贡献最小,使得单次转移只和当前前面有几个有关,我们如何做到?实际上观察表达式,应该是每对要求的字母后面的位置减前面的位置的和,也就是\(cnt_{x,y}*(pos_{x}-pos_{y})\)这里我们为了达到无后效性,算每个单词的“贡献”。把每个单词的加入分成两个部分,一部分是之前有多少个cnt产生了什么影响,一个是之后有多少个cnt产生了什么影响。说的混乱,可以看作是利用差分达到计算对最终答案的贡献。枚举最后插入的字符,转移方程为\(dp[x]=min(dp[x],popcount(new\_x)*(\sum_{j\in new\_x}cnt_{i,j}-\sum_{j\notin new\_x}cnt_{i,j}))\)具体看代码就好了。

代码:

#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define PB push_back
#define LL long long
#define pii pair<int,int>
#define MEM(x,y) memset(x,y,sizeof(x))
#define bug(x) cout<<"debug "#x" is "<<x<<endl;
#define FIO ios::sync_with_stdio(false);
#define ALL(x) x.begin(),x.end()
#define LOG 20
const int inf =0x3f3f3f3f;
const int maxn =(1<<20)+7;
int dp[maxn];
map<int,int> h;
int sum[maxn][22];
int cnt[22][22];
int n,m;
string s;
int lower_bit(int x){return x&-x;};

int get_sum(int x,int i){
    if(sum[x][i]!=-1)return sum[x][i];
    if(x==0) return sum[x][i]=0;
    sum[x][i]=get_sum(x&(x-1),i)+cnt[h[lower_bit(x)]][i];
    return sum[x][i];
}

int DP(int x){
    if(dp[x]!=-1) return dp[x];
    if(x==0)return dp[x]=0;
    int t=x;
    dp[x]=inf;
    for(int i=0;i<m;i++){
        if(x&(1<<i)){
            int p=x^(1<<i);
            int q=x^((1<<m)-1);
            dp[x]=min(dp[x],DP(p)+__builtin_popcount(p)*(get_sum(p,i)-get_sum(q,i)));
        }
    }
    return dp[x];
}

int main(){
    FIO;
    cin>>n>>m>>s;
    MEM(dp,-1);MEM(sum,-1);
    for(int i=0;i<m;i++) h[1<<i]=i;
    for(int i=1;i<n;i++){
        ++cnt[s[i] - 'a'][s[i - 1] - 'a'];
        ++cnt[s[i - 1] - 'a'][s[i] - 'a'];
    }
    cout<<DP((1<<m)-1)<<endl;
    return 0;
}

posted @ 2019-10-11 17:21 zhangxianlong 阅读(...) 评论(...) 编辑 收藏