zoukankan      html  css  js  c++  java
  • 状态压缩dp增量统计贡献——cf1238E(好题)

    这题的状态设计非常巧妙,因为dp[S]表示的并非当前正确的值,而是维护一个中间量,这个中间量在到达末状态时才正确

    当然官方的题解其实更加直观,只不过理解起来其实有点困难

    /*
    给定一个串s,字符集为20,求一个长为m的序列t,设pos[ch]为ch在t中的位置
    确定一个t使得sum{ |pos[s[i]]-pos[s[i+1]]| }
    
    先预处理cnt[][]数组用来存s中各种字符对的数量
    然后进行状态压缩dp,从左到右按位确定t,S表示已经用掉的字符状态
    用增量法对每种字符单独统计贡献,新增一个字符时,所有未在集合中的字符和已经在集合中的字符所形成的对数的贡献都会+1 
    dp[S]表示字符集是S时的贡献,那么对于每个S,都可以更新S|(1<<i)的状态 
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define N 200005
    #define ll long long 
    
    int n,m;
    char s[N];
    ll dp[1<<21],cnt[21][21];
    
    int main(){
        //int t;cin>>t;while(t--){
            scanf("%d%d",&n,&m);
            scanf("%s",s);
            int len=strlen(s);
            memset(cnt,0,sizeof cnt);
            for(int i=0;i<len-1;i++){
                cnt[s[i]-'a'][s[i+1]-'a']++;
            }
            
            for(int S=0;S<(1<<m);S++)dp[S]=0x3f3f3f3f;
            dp[0]=0;
            for(int S=0;S<(1<<m);++S){
                ll tot=0;//在状态S下新加入任意一个点 
                for(int i=0;i<m;i++)if(!(S>>i & 1)){
                    for(int j=0;j<m;j++)if(S>>j & 1)
                        tot+=cnt[i][j]+cnt[j][i];
                }
                
                for(int i=0;i<m;i++)if(!(S>>i & 1))
                    dp[S|(1<<i)]=min(dp[S|(1<<i)],dp[S]+tot); 
            }
            
            cout<<dp[(1<<m)-1]<<'
    ';
        //}
    }
  • 相关阅读:
    【题解】[HEOI2016/TJOI2016]字符串
    【题解】CF1037H Security
    Centos 7开机自启动oracle
    WRH$_ACTIVE_SESSION_HISTORY打补丁14084247实现自动分区
    oracle设置awr采集间隔和保留时间
    dgbroker配置Fast-Start Failover
    dgbroker删除后切换为手工管理
    删除dgbroker
    现有dgbroker管理的dg下添加一台从库且互相切换
    linux-unzip-error
  • 原文地址:https://www.cnblogs.com/zsben991126/p/11705832.html
Copyright © 2011-2022 走看看