Loading [MathJax]/extensions/tex2jax.js

POJ1037:A decorative fence 题解

主要思路

这道题是计数类 DP 中一道非常好的题。

我先来简述一下“试填法”。很多做过数位 DP 的神仙们都可能比较熟悉这样的技巧,一次次相减尝试并统计答案。这就是试填法,特别是在一些递推形式的数位 DP 中会经常简单这样的方法。

那我们开始来分析一下这道题吧。首先,设\(F[i][j][0/1]\)为木板数为\(i\)时,最后放置的木板排名为\(j\)且处于低位(0)或高位(1)。可以写出递推式:

\[ F[i][j][0] = \sum_{h=j}^{i-1} F[i-1][h][1] \\ F[i][j][1] = \sum_{h = 1}^{j-1} F[i-1][h][0] \]

之后,我们可以试图找出第一块木板的排名,设\(last\)为上一块木板的排名,\(k\)为上一块木板是否为高位(0/1)。通过枚举高度并试填:

  • 当枚举到的方案数小于目前的方案余量,余量减去并继续枚举。
  • 当方案数大于方案余量,记下状态并跳出。

对于之后的\([2,n]\)块木板,道理是一样的。需要注意的是,我们的枚举顺序从\(n\)到\(1\),保证完整性。

代码

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// POJ1037.cpp
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
ll n, m, T, f[25][25][2];
bool vis[25];
void initialize()
{
f[1][1][0] = f[1][1][1] = 1;
for (int i = 1; i <= 20; i++)
for (int j = 1; j <= i; j++)
{
for (int k = j; k <= i - 1; k++)
f[i][j][0] += f[i - 1][k][1];
for (int k = 1; k <= j - 1; k++)
f[i][j][1] += f[i - 1][k][0];
}
}
int main()
{
scanf("%d", &T);
initialize();
while (T--)
{
memset(vis, 0, sizeof(vis));
scanf("%lld%lld", &n, &m);
// to determine the first condition;
ll last, k;
for (int j = 1; j <= n; j++)
{
if (f[n][j][1] >= m)
{
last = j, k = 1;
break;
}
else
m -= f[n][j][1];
if (f[n][j][0] >= m)
{
last = j, k = 0;
break;
}
else
m -= f[n][j][0];
}
vis[last] = true;
printf("%lld", last);
for (int i = 2; i <= n; i++)
{
k ^= 1;
int j = 0;
for (int len = 1; len <= n; len++)
{
if (vis[len])
continue;
j++;
if ((k == 1 && len > last) || (k == 0 && len < last))
if (f[n - i + 1][j][k] >= m)
{
last = len;
break;
}
else
m -= f[n - i + 1][j][k];
}
printf(" %lld", last);
vis[last] = true;
}
puts("");
}
return 0;
}
// POJ1037.cpp #include <cstdio> #include <algorithm> #include <cstring> #define ll long long using namespace std; ll n, m, T, f[25][25][2]; bool vis[25]; void initialize() { f[1][1][0] = f[1][1][1] = 1; for (int i = 1; i <= 20; i++) for (int j = 1; j <= i; j++) { for (int k = j; k <= i - 1; k++) f[i][j][0] += f[i - 1][k][1]; for (int k = 1; k <= j - 1; k++) f[i][j][1] += f[i - 1][k][0]; } } int main() { scanf("%d", &T); initialize(); while (T--) { memset(vis, 0, sizeof(vis)); scanf("%lld%lld", &n, &m); // to determine the first condition; ll last, k; for (int j = 1; j <= n; j++) { if (f[n][j][1] >= m) { last = j, k = 1; break; } else m -= f[n][j][1]; if (f[n][j][0] >= m) { last = j, k = 0; break; } else m -= f[n][j][0]; } vis[last] = true; printf("%lld", last); for (int i = 2; i <= n; i++) { k ^= 1; int j = 0; for (int len = 1; len <= n; len++) { if (vis[len]) continue; j++; if ((k == 1 && len > last) || (k == 0 && len < last)) if (f[n - i + 1][j][k] >= m) { last = len; break; } else m -= f[n - i + 1][j][k]; } printf(" %lld", last); vis[last] = true; } puts(""); } return 0; }
// POJ1037.cpp
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
ll n, m, T, f[25][25][2];
bool vis[25];
void initialize()
{
    f[1][1][0] = f[1][1][1] = 1;
    for (int i = 1; i <= 20; i++)
        for (int j = 1; j <= i; j++)
        {
            for (int k = j; k <= i - 1; k++)
                f[i][j][0] += f[i - 1][k][1];
            for (int k = 1; k <= j - 1; k++)
                f[i][j][1] += f[i - 1][k][0];
        }
}
int main()
{
    scanf("%d", &T);
    initialize();
    while (T--)
    {
        memset(vis, 0, sizeof(vis));
        scanf("%lld%lld", &n, &m);
        // to determine the first condition;
        ll last, k;
        for (int j = 1; j <= n; j++)
        {
            if (f[n][j][1] >= m)
            {
                last = j, k = 1;
                break;
            }
            else
                m -= f[n][j][1];
            if (f[n][j][0] >= m)
            {
                last = j, k = 0;
                break;
            }
            else
                m -= f[n][j][0];
        }
        vis[last] = true;
        printf("%lld", last);
        for (int i = 2; i <= n; i++)
        {
            k ^= 1;
            int j = 0;
            for (int len = 1; len <= n; len++)
            {
                if (vis[len])
                    continue;
                j++;
                if ((k == 1 && len > last) || (k == 0 && len < last))
                    if (f[n - i + 1][j][k] >= m)
                    {
                        last = len;
                        break;
                    }
                    else
                        m -= f[n - i + 1][j][k];
            }
            printf(" %lld", last);
            vis[last] = true;
        }
        puts("");
    }
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *