• 周四. 8月 11th, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

uva 12371 Guards (dp)

admin

11月 28, 2021

题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3793

首先发现按照规定的摆放方法一定会有圈,其中每个圈的相邻两行都有一对棋子位于同一列上

(dp[i][j]) 表示 (i*i) 的棋盘上有 (j) 个圈的方案数,则转移的时候分增不增加圈数来讨论

  • 不增加圈数

(i*i) 这个格子有没有守卫来讨论

如果有,只需从前 (i-1) 行中选一行,将右侧的守卫移动到最后一列,再在最后一行的相应位置增加一个守卫即可,对应的转移方程为 (dp[i][j] += dp[i-1][j]*(i-1))

如果没有,需要从前 (i-1) 列选择一列,将该列两个守卫移动到最后一列中同样的位置,然后在前 (i-1) 行选择一行,将左侧的守卫移动到之前选择的一列的最后一行,再加上最后一行剩余的守卫即可,对应的转移方程为 (dp[i][j] += dp[i-1][j] * (i-1) * (i-1))

  • 增加圈数

由上面的讨论可知,还有一种情况是增加一个 (2*2) 的圈,方法是在最后一行选择两个位置作为下半部分,再在前 (i-1) 行选择一行作为上半部分,对应的转移方程为 (dp[i][j] += dp[i-2][j-1] * C_n^2 * (i-1))

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 100010;
const int maxk = 55;
const int M = 1000000007;

int T, n, k;
int dp[maxn][maxk];

int sqr(ll x){
	x %= M;
	return 1ll * x * x % M;
}

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	dp[2][1] = 1;
	for(int i = 3 ; i <= 100000 ; ++i){
		for(int j = 1 ; j <= 50 ; ++j){
			dp[i][j] = 1ll * dp[i-1][j] * (i-1) % M * i % M;
			dp[i][j] = (dp[i][j] + 1ll * dp[i-2][j-1] * (1ll * i * (i-1)/2) % M * (i-1) % M) % M;
		}
	}
	
	scanf("%d", &T);
	for(int kase = 1 ; kase <= T ; ++kase){
		scanf("%d%d", &n, &k);
		printf("Case %d: %d
", kase, dp[n][k]);
	}
	return 0;
}

发表评论

您的电子邮箱地址不会被公开。