《算法进阶指南》--计数类DP--装饰围栏
计数类DP通常对不从不漏的要求严格,一般围绕一个基本点展开。这题的基本点在于,当前i个木板确定,后面剩余的木板的计数依旧符合n个木板的计数方法,需要枚举第一个数及其属于高位或低位进行转移。用f[i][j][k]表示长度为i,第一个数为第j大,处于k0高位或k1低位的数量。
int f[22][22][2];
void init()
{
f[1][1][0] = 1, f[1][1][1] = 1;
for (int i = 2; i <= 20; i++)
{
for (int j = 1; j <= i; j++)
{
for (int k = 0; k <= 1; k++)
{
if (k == 0)
{
for (int jj = j; jj < i; jj++)
{
f[i][j][k] += f[i - 1][jj][k ^ 1];
}
}
else
{
for (int jj = 1; jj < j; jj++)
{
f[i][j][k] += f[i - 1][jj][k ^ 1];
}
}
}
}
}
}
int ans[22];
int vis[22] = {0};
void solve()
{
init();
int q;
cin >> q;
int n, c;
while (q--)
{
memset(vis, 0, sizeof(vis));
cin >> n >> c;
int lastlen, lastk;
for (int j = 1; j <= n; j++)
{
int ok = 0;
for (int k = 1; k >= 0; k--)
{
if (f[n][j][k] >= c)
{
lastlen = j;
lastk = k;
ok = 1;
break;
}
else
{
c -= f[n][j][k];
}
}
if (ok == 1)
{
break;
}
}
cout << lastlen << ' ';
vis[lastlen] = 1;
for (int i = 2; i <= n; i++)
{
int j = 0;
for (int len = 1; len <= n; len++)
{
if (vis[len])
{
continue;
}
j++;
if (lastk == 0 && len > lastlen || lastk == 1 && len < lastlen)
{
if (f[n - i + 1][j][lastk ^ 1] >= c)
{
lastlen = len;
lastk ^= 1;
cout << len << ' ';
break;
}
else
{
c -= f[n - i + 1][j][lastk ^ 1];
}
}
}
}
cout << endl;
}
}
收获:
对于求第c个大的具体方案,c为一个极大数。通常用倍增的思想不断逼近c。

浙公网安备 33010602011771号