1. 程式人生 > >[洛谷P2750] [USACO5.5]貳五語言Two Five

[洛谷P2750] [USACO5.5]貳五語言Two Five

是否 size usaco 程序 但是 def 滿足 距離 它的

洛谷題目鏈接:[USACO5.5]貳五語言Two Five

題目描述

有一種奇怪的語言叫做“貳五語言”。它的每個單詞都由A~Y這25個字母各一個組成。但是,並不是任何一種排列都是一個合法的貳五語言單詞。貳五語言的單詞必須滿足這樣一個條件:把它的25個字母排成一個5*5的矩陣,它的每一行和每一列都必須是遞增的。比如單詞ACEPTBDHQUFJMRWGKNSXILOVY,它排成的矩陣如下所示:

A C E P T

B D H Q U

F J M R W

G K N S X

I L O V Y

因為它的每行每列都是遞增的,所以它是一個合法的單詞。而單詞YXWVUTSRQPONMLKJIHGFEDCBA則顯然不合法。 由於單詞太長存儲不便,需要給每一個單詞編一個碼。編碼方法如下:從左到右,再從上到下,可以由一個矩陣的得到一個單詞,再把單詞按照字典順序排序。比如,單詞ABCDEFGHIJKLMNOPQRSTUVWXY的編碼為1,而單詞ABCDEFGHIJKLMNOPQRSUTVWXY的編碼為2。

現在,你需要編一個程序,完成單詞與編碼間的轉換。

輸入輸出格式

輸入格式:

第一行為一個字母N或W。N表示把編碼轉換為單詞,W表示把單詞轉換為編碼。

若第一行為N,則第二行為一個整數,表示單詞的編碼。若第一行為W,則第二行為一個合法的單詞。

輸出格式:

每行一個整數或單詞。

輸入輸出樣例

輸入樣例#1:

N
2

輸出樣例#1:

ABCDEFGHIJKLMNOPQRSUTVWXY

輸入樣例#2:

W
ABCDEFGHIJKLMNOPQRSUTVWXY

輸出樣例#2:

2

說明

題目翻譯來自NOCOW。

USACO Training Section 5.5

題解: 我嘗試過爆搜,但是在我\(A\)

了這題之後都還沒枚舉完所有情況...

顯然這個狀態是非常多的,接近\(25!\),所以我們需要將其中一些狀態記憶化一下.

我們設\(f[a][b][c][d][e]\)表示第\(1\)行填入了\(a\)個數,第\(2\)行填入了\(b\)個數....的方案.因為我們在填數的時候一定是一個像這樣的形狀(轉自zyzzyzzyzzyz的圖):
技術分享圖片

要填入綠色的塊之前一定要先填入黃色的塊.

那麽我們可以計算出方案數之後,就考慮如何計算答案.我們可以通過類似倍增求\(lca\)的方式來統計答案.回憶一下倍增是如何求\(lca\)的?從大到小枚舉向上跳的距離,如果超過了就不跳.這裏也是類似的方法.

我們在求方案數的時候會用一個\(vis\)

數組來記錄某個數字是否可以用,那麽在我們修改限制的時候,每次計算出的方案數很顯然是會不一樣的,這時候就可以用我們上面提到的方法了.

具體細節有點講不太清,看代碼吧.

#include<bits/stdc++.h>
using namespace std;

int n, vis[30];
int f[7][7][7][7][7];
char opt, s[30];

bool ok(int x, int num){
    return !vis[x] || vis[x] == num;
}

int dfs(int a, int b, int c, int d, int e, int x){
    if(x == 26) return 1;
    if(f[a][b][c][d][e]) return f[a][b][c][d][e];
    int res = 0;
    if(a <= 5 && ok(a, x)) res += dfs(a+1, b, c, d, e, x+1);
    if(b < a && ok(b+5, x)) res += dfs(a, b+1, c, d, e, x+1);
    if(c < b && ok(c+10, x)) res += dfs(a, b, c+1, d, e, x+1);
    if(d < c && ok(d+15, x)) res += dfs(a, b, c, d+1, e, x+1);
    if(e < d && ok(e+20, x)) res += dfs(a, b, c, d, e+1, x+1);
    return f[a][b][c][d][e] = res;
}

int main(){
    int tmp; cin >> opt;
    if(opt == 'N'){
        cin >> n;
        for(int i = 1; i <= 25; i++){
            for(vis[i] = 1; ; vis[i]++){
                memset(f, 0, sizeof(f));
                tmp = dfs(1, 1, 1, 1, 1, 1);
                if(tmp >= n) break;
                n -= tmp;
            }
        }
        for(int i = 1; i <= 25; i++) cout << (char)(vis[i]-1+'A');
        cout << endl;
    }
    else {
        int res = 0; cin >> s+1;
        for(int i = 1; i <= 25; i++)
            for(vis[i] = 1; vis[i] < s[i]-'A'+1; vis[i]++){
                memset(f, 0, sizeof(f));
                res += dfs(1, 1, 1, 1, 1, 1);
            }
        cout << res+1 << endl;
    }
    return 0;
}

[洛谷P2750] [USACO5.5]貳五語言Two Five