CF E74 E. Keyboard Purchase //状压dp+贡献

题目

题意

给一字符串 S S S( m m m种字符),然后你要做一个键盘(一行 m m m列,每个字符用一次),敲打字符串的代价为手移动的距离和( ∑ 1 l e − 1 ∣ p o s [ s [ i ] ] − p o s [ s [ i − 1 ] ] ∣ \sum_{1}^{le-1}|pos[s[i]]-pos[s[i-1]]| 1le1pos[s[i]]pos[s[i1]] p o s x pos_x posx x x x在键盘上的位置)。求敲打出 S S S最少的代价。

思路

我一开始想的是暴力逐位加入字符,然后计算当前已有的 i i i个字符的代价,这样需要考虑到前面 i − 1 i-1 i1位字符的排列,显然是错的想法。
正解:
答案其实是: ∑ i = 0 m a x c h a r ∑ j = 0 m a x c h a r (   ( p o s [ i ] − p o s [ j ] ) ∗ c n t [ i ] [ j ]   ) \sum_{i=0}^{maxchar} \sum_{j=0}^{maxchar} (\ (pos[i]-pos[j])*cnt[i][j]\ ) i=0maxcharj=0maxchar( (pos[i]pos[j])cnt[i][j] ), c n t [ i ] [ j ] cnt[i][j] cnt[i][j]是原串中字符 i i i j j j相邻的个数。
这样,暴力逐位加入字符的时候,考虑所有二元对 < x , y > <x,y> <x,y>,如果 x x x是已经确定的字符, y y y是未确定的字符,那么这二元对,就会对答案有贡献 c n t [ x ] [ y ] cnt[x][y] cnt[x][y](相当于距离+1)。
复杂度 O ( 2 m ∗ m 2 ) O(2^m*m^2) O(2mm2)
注意第一层循环枚举的是状态会好些很多。

/*   Author : Rshs
 *   Data : 2019-10-09-00.19
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 1e9+7;
const int MXN = 1e6+5;
int mp[30][30];
char s[MXN];
int dp[(1<<21)];
int main(){
    int n,m;cin>>n>>m;
    scanf("%s",s);
    for(int i=1;i<n;i++)
        mp[s[i]-'a'][s[i-1]-'a']++,mp[s[i-1]-'a'][s[i]-'a']++;
    for(int i=0;i<=(1<<m);i++) dp[i]=INT_MAX/10;
    int NN=(1<<m);dp[0]=0;
    for(int i=1;i<NN;i++){
        for(int j=0;j<m;j++){ //用之前的更新当前的
            if( ((1<<j)&i) == 0) continue;
            dp[i]=min(dp[i],dp[i-(1<<j)]);
        }
        for(int j=0;j<m;j++){//暴力枚举二元对
            if( ((1<<j)&i) ==0 )continue;
            for(int k=0;k<m;k++){
                if( ((1<<k)&i) >0) continue;
                dp[i]+=mp[j][k];
            }
        }
    }
    cout<<dp[(1<<m)-1];
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值