一名苦逼的OIer,想成为ACMer

Iowa_Battleship

POJ1037 A Decorative Fence

一道计数类\(DP\)

原题链接

先用\(DP\)计算方案数,再枚举木板,并将\(C\)不断减去方案数,直到得出所求方案。
定义\(f[i][j][k]\)表示用\(i\)块木板构成栅栏,其中最左边的木板高度从小到大排在第\(j\)块,\(k\)表示这块木板是低位还是高位(低位为\(0\),高位为\(1\))。

\(\qquad\qquad f[i][j][0]=\sum\limits_{k=j}^{i-1}f[i-1][k][1]\)

\(\qquad\qquad f[i][j][1]=\sum\limits_{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("\n");
	}
	return 0;
}

posted on 2018-08-19 19:27  Iowa_Battleship  阅读(258)  评论(1编辑  收藏  举报

导航