一道计数类(DP)
原题链接
先用(DP)计算方案数,再枚举木板,并将(C)不断减去方案数,直到得出所求方案。
定义(f[i][j][k])表示用(i)块木板构成栅栏,其中最左边的木板高度从小到大排在第(j)块,(k)表示这块木板是低位还是高位(低位为(0),高位为(1))。
(qquadqquad f[i][j][0]=sumlimits_{k=j}^{i-1}f[i-1][k][1])
(qquadqquad f[i][j][1]=sumlimits_{k=1}^{j-1}f[i-1][k][0])
然后就可以枚举木板,当枚举到第(i)块木板,这块木板剩余木板中排名为(j),若(f[n-i+1][j][k]<C),则将(C)减去(f[n-i+1][j][k]),否则就说明第(i)个位置就是这块木板。
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 22;
typedef long long ll;
ll f[N][N][2];
bool v[N];
ll re()
{
ll x = 0;
char c = getchar();
bool p = 0;
for (; c<'0' || c>'9'; c = getchar())
p = (c == '-' || p) ? 1 : 0;
for (; c >= '0'&&c <= '9'; c = getchar())
x = x * 10 + (c - '0');
return p ? -x : x;
}
int main()
{
int i, j, k, n, t, la, o;
ll m;
t = re();
f[1][1][0] = f[1][1][1] = 1;
for (i = 2; i <= 20; i++)
for (j = 1; j <= i; j++)
{
for (k = 1; k < j; k++)
f[i][j][1] += f[i - 1][k][0];
for (k = j; k < i; k++)
f[i][j][0] += f[i - 1][k][1];
}
while (t--)
{
n = re();
m = re();
memset(v, 0, sizeof(v));
for (i = 1; i <= n; i++)
{
if (f[n][i][1] >= m)
{
la = i;
k = 1;
break;
}
else
m -= f[n][i][1];
if (f[n][i][0] >= m)
{
la = i;
k = 0;
break;
}
else
m -= f[n][i][0];
}
v[la] = 1;
printf("%d", la);
for (i = 2; i <= n; i++)
{
k ^= 1;
o = 0;
for (j = 1; j <= n; j++)
if (!v[j])
{
o++;
if ((!k&&j < la) || (k&&j > la))
if (f[n - i + 1][o][k] >= m)
{
la = j;
break;
}
else
m -= f[n - i + 1][o][k];
}
v[la] = 1;
printf(" %d", la);
}
printf("
");
}
return 0;
}