Codeforces1238E. Keyboard Purchase(狀壓dp + 計算貢獻)


題目鏈接:傳送門


 

思路:

題目中的m為20,而不是26,顯然在瘋狂暗示要用狀壓來做。

考慮狀壓字母集合。如果想要保存字母集合中的各字母的順序,那就和經典的n!的狀態的狀壓沒什么區別了,時間復雜度為O(m22m),是不可行的,所以本題肯定有更好的做法。

考慮不保存字母集合中各字母的順序。那么問題來了,新加入一個字母后,要如何計算這個新的字母對slowness產生的影響呢?

不妨設當前已經被選過的字母集合為i(0 ≤ i ≤ (1<<m)),當前要加入的字母j(0 ≤ j < m),且(i>>j&1) == 0。

考慮每次都把新的字母j放在i中的所有字母的右邊,則字母j的加入對答案的影響為:

$\sum_{k\in i}cnt_{j,k}*(pos_{j}-pos_{k}) +\sum_{k\notin i}cnt_{j,k}*(pos_{k}-pos_{j}) $,其中$cnt_{j, k}$表示輸入密碼時,從j移動到k和從k移動到j的次數之和

其中,$pos_{j}$已知,為i中1的個數,但是pos_{k}因為沒有記錄i中各個字母的順序,無法得知。

那么我們不妨只直接計算字母j對應的$pos_{j}$對答案產生的影響:$\sum_{k\in i}cnt_{j,k}*pos_{j}-\sum_{k\notin i}cnt_{j,k}*pos_{j}$

這樣的做法還是O(m22m),但是預處理出這個東西$\sum_{k\in i}cnt_{j,k}*pos_{j}$,就可以把時間復雜度優化到O(m2m)了。


 

代碼:O(m2m)

#include <bits/stdc++.h>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define N 100005
#define M 20
#define INF 0x3f3f3f3f
#define mk(x) (1<<x) // be conscious if mask x exceeds int
#define sz(x) ((int)x.size())
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)

using namespace std;
typedef long long ll;
typedef double db;

/** fast read **/
template <typename T>
inline void read(T &x) {
    x = 0; T fg = 1; char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-') fg = -1;
        ch = getchar();
    }
    while (isdigit(ch)) x = x*10+ch-'0', ch = getchar();
    x = fg * x;
}
template <typename T, typename... Args>
inline void read(T &x, Args &... args) { read(x), read(args...); }

string s;
int f[mk(M)], cnt[mk(M)];
int main()
{
    int n, m; read(n, m);
    cin >> s;
    for (int i = 1; i < n; i++) {
        int l = s[i-1] - 'a', r = s[i] - 'a';
        cnt[mk(l) | mk(r)]++;
    }
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < mk(m); j++) {
            if (j & mk(i))
                cnt[j] += cnt[j ^ mk(i)];
        }
    }
    memset(f, 0x3f, sizeof f);
    f[0] = 0;
    for (int i = 0; i < mk(m); i++) {
        for (int j = 0; j < m; j++) {
            if ((i & mk(j)) == 0) {
                int add = n-1;
                add -= cnt[i ^ mk(j)] + cnt[mk(m)-1 - (i ^ mk(j))];
                f[i ^ mk(j)] = min(f[i ^ mk(j)], f[i] + add);
            }
        }
    }
    cout << f[mk(m)-1] << endl;
    return 0;
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM