【USACO 5.5.3】Two Five

题意

  一个5*5字符矩阵,每个字符比它左面和上面的字符都要大,它可以这样转换成一个字符串:下一行接在本行行末。所以是一个长度为25的字符串(包含A~Y的字符)。把符合条件的矩阵转换成字符串后,按字典序排序并依次编号。要求编号和字符串互相转换。

题解

  刚开始就觉得以前做过很多类似这样的题了。但是它的限制条件相对来说比较多,也没有什么特殊规律。所以只好计数DP了。

  先看编号转字符串,枚举字符串开头(即一行一行地填字符矩阵),比如说“AB”开头的字符串有多少个?如果大于要求的编号的话,那么自然答案字符串的开头就是“AB”了。这个思想十分容易理解。

  字符串转编号,类似地!如果这个字符串是“AC~~~~~”,我们先加上"AB~~~~"的字符串个数,然后一直逼近,直至相同。

  好了。那么怎么统计以“AB”或者其他开头的字符串个数呢?

  搞个5维数组DP。每一维表示这一行填了多少个。那么F[5][5][5][5][5]=1了。此乃边界是也。

  具体看下代码,比较好理解啦。

  哦对对,算下时间复杂度先。O(55)*O(52)=O(57)=O(78125)

代码

/*
TASK:twofive
LANG:C++
*/
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int f[6][6][6][6][6], code, len[5], t[5], mode, maxr[5], maxc[5];
char cmd[2], chmat[5][5];
bool v[25];

int dp(int a, int b, int c, int d, int e, int k)
{
    int &ans = f[a][b][c][d][e];
    if (ans) return ans;
    if (!v[k]) return dp(a, b, c, d, e, k + 1);
    if (a < 5 && k > maxr[0] && k > maxc[a]) ans += dp(a+1, b, c, d, e, k+1);
    if (b < a && k > maxr[1] && k > maxc[b]) ans += dp(a, b+1, c, d, e, k+1);
    if (c < b && k > maxr[2] && k > maxc[c]) ans += dp(a, b, c+1, d, e, k+1);
    if (d < c && k > maxr[3] && k > maxc[d]) ans += dp(a, b, c, d+1, e, k+1);
    if (e < d && k > maxr[4] && k > maxc[e]) ans += dp(a, b, c, d, e+1, k+1);
    return ans;
}

int main()
{
    freopen("twofive.in", "r", stdin);
    freopen("twofive.out", "w", stdout);
    scanf("%s\n", cmd);
    memset(len, 0, sizeof(len));
    code = 0;
    memset(chmat, 0, sizeof(chmat));
    if (cmd[0] == 'N') scanf("%d", &code), mode = 0;
    else
    {
        for (int i = 0; i < 5; ++i)
            for (int j = 0; j < 5; ++j)
                scanf("%c", &chmat[i][j]);
        mode = 1;
    }
    memset(v, true, sizeof(v));
    memset(maxr, -1, sizeof(maxr));
    memset(maxc, -1, sizeof(maxc));
    for (int i = 0; i < 5; ++i)
    {
        len[i] = 0;
        for (int j = 0; j < 5; ++j)
        {
            len[i]++;
            if (mode == 0)
            {
                for (int k = 0; k < 25; ++k)
                    if (v[k] && k > maxr[i] && k > maxc[j])
                    {
                        memset(f, 0, sizeof(f));
                        f[5][5][5][5][5] = 1;
                        v[k] = false;
                        maxr[i] = maxc[j] = k;
                        int tmp = dp(len[0], len[1], len[2], len[3], len[4], 0);
                        if (code <= tmp)
                        {
                            chmat[i][j] = 'A' + k;
                            break;
                        }
                        else
                        {
                            v[k] = true;
                            code -= tmp;
                        }
                    }
            }
            else
            {
                for (int k = 0; k < chmat[i][j] - 'A'; ++k)
                    if (v[k] && k > maxr[i] && k > maxc[j])
                    {
                        memset(f, 0, sizeof(f));
                        f[5][5][5][5][5] = 1;
                        v[k] = false;
                        maxr[i] = maxc[j] = k;
                        code += dp(len[0], len[1], len[2], len[3], len[4], 0);
                        v[k] = true;
                    }
                v[chmat[i][j] - 'A'] = false;
            }
        }
    }
    if (mode == 0)
    {
        for (int i = 0; i < 5; ++i)
            for (int j = 0; j < 5; ++j)
                printf("%c", chmat[i][j]);
        printf("\n");
    }
    else printf("%d\n", code + 1);
    return 0;
}

 

posted @ 2016-04-02 13:59  albertxwz  阅读(521)  评论(0编辑  收藏  举报