51Nod B 小明与回转寿司V2

传送门


题目

小明来到一家新开的日料店品尝回转寿司。
料理师傅共准备了26种不同的寿司,记为a~z。每次他都会选出每种寿司各一枚,按照一定顺序(如mildreabcfghjknopqstuvwxyz)放到传送带上,记为一轮。之后每轮,师傅都会按照同样的顺序将寿司放上传送带。
由于店内客人较多,寿司到达小明面前时已经所剩无几。小明记录了一段时间内经过自己面前的每一枚寿司的种类,现在他想知道,这期间至少有多少轮寿司经过了自己面前?

题解

比较摆烂,随便写写。
状压, 一开始每个字符单独一轮,考虑dp长度为20的那个字符串,加入第一个字母,假如是a,那么所有出现的a都可以把它后面的那个字母并入自己,同理,每加入一个字符,这个字符每次出现的位置,如果它后面的字符在原始字符串中也在它后面,那就并入;
预处理一个数组,\(pre_{a,b}\)表示a和b相邻,且a在b前面出现的次数。

这题大有可以研究的地方,诸如复用,无效信息等等,很神奇的是答案之和pre数组有关。
今日摆烂,下次一定

实现

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

int read(){
    int num=0, flag=1; char c=getchar();
    while(!isdigit(c) && c!='-') c=getchar();
    if(c == '-') c=getchar(), flag=-1;
    while(isdigit(c)) num=num*10+c-'0', c=getchar();
    return num*flag;
}


const int N = 1e5+100;
const int Z = 2e6;
const int M = 21;
const int S = 26;
int vis[S], tab[S],cnt=0, s[N], n, pre[M][M];
vector<int> zt[M];
int f[Z];

void reads(){
    char c=getchar();
    while(c>='a' && c<='z') s[++n] = c-'a', c=getchar();
}

void dfs(int lzt, int x, int num){
    if(x >= cnt){
        zt[num].push_back(lzt);
        return ;
    }
    dfs(lzt, x+1, num);
    dfs(lzt+(1<<x), x+1, num+1);
}

int main(){
    reads();
    for(int i=1; i<=n; i++) vis[s[i]]=1;
    for(int i=0; i<S; i++) if(vis[i]) tab[i]=cnt++;
    for(int i=1; i<=n; i++) s[i] = tab[s[i]];
    for(int i=1; i<n; i++) pre[s[i]][s[i+1]]++;
    dfs(0, 0, 0);

    for(int i=0; i<Z; i++) f[i]=n;
    for(int i=1; i<=cnt; i++){
        for(auto j : zt[i]){
            for(int k=0; k<cnt; k++){
                if(!(j & (1<<k)) ) continue;
                int lzt = (j - (1<<k));
                
                int res = 0;
                for(int l=0; l<cnt; l++){
                    if(j & (1<<l)) continue;
                    res += pre[k][l];
                }   

                f[j] = min(f[j], f[lzt]-res);

            }
        }
    }
    
    cout << f[zt[cnt][0]];

    return 0;
}           

posted @ 2022-02-27 09:47  ltdJcoder  阅读(133)  评论(0编辑  收藏  举报