zoukankan      html  css  js  c++  java
  • AHOI 2009 中国象棋

    题面

    题目描述

    这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!

    输入输出格式

    输入格式:

    一行包含两个整数N,M,之间由一个空格隔开。

    输出格式:

    总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。

    输入输出样例

    输入样例#1:

    1 3
    

    输出样例#1:

    7
    

    说明

    样例说明

    除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有222-1=7种方案。

    数据范围

    100%的数据中N和M均不超过100

    50%的数据中N和M至少有一个数不超过8

    30%的数据中N和M均不超过6

    Solution

    乍看到这题, 真的没什么思路.

    找到的比较靠谱的解法是这样的: 我们从上往下一行一行放置棋子, 用f[i][j][k]表示在前(i)行中, (j)列上有(1)个棋子, (k)列上有(2)个棋子的合法方案数. 考虑怎么转移: 开始时我考虑的是递归式, 发现式子的形式非常复杂, 有许多细节需要考虑, 因此改为递推式.

    考虑在已经放置好的前(i)列的基础上, 在第(i + 1)列上放棋子. 我们可以在这一行上放(0)(1)(2)颗棋子, 并且要求这些棋子所在的列原来最多只能有(1)颗棋子. 暴力转移即可. 时间复杂度: (O(nm^2)).

    #include <cstdio>
    #include <cstring>
    
    typedef long long LL;
    const int N = 100, M = 100, MOD = 9999973;
    int n, m;
    int f[N + 7][M + 7][M + 7];
    inline void plus(int &a, LL b) { a = (a + b) % MOD; }
    int main()
    {
    	scanf("%d%d", &n, &m);
    	memset(f, 0, sizeof f);
    	f[0][0][0] = 1;
    	for (int i = 0; i < n; ++ i) for (int j = 0; j <= m; ++ j) for (int k = 0; k <= m; ++ k) if (f[i][j][k])
    	{
    /*		f[i][j][k] = f[i - 1][j][k];
    		if (j) plus(f[i][j][k], (LL)f[i - 1][j - 1][k] * (m - j - k + 1));
    		if (k) plus(f[i][j][k - 1], (LL)f[i - 1][j + 1][k - 1] * (j + 1));
    		if (j >= 2) plus(f[i][j - 2][k], (LL)f[i - 1][j - 2][k] * (m - j - k + 2) * (m - j - k + 1) / 2);
    		if (k >= 2) plus(f[i][j][k], (LL)f[i - 1][j + 2][k - 2] * (j + 2) * (j + 1) / 2);
    		if (j && k) plus(f[i][j][k], (LL)f[i - 1][j][k - 1] * (m - j - k + 1) * j); */
    		plus(f[i + 1][j][k], f[i][j][k]);
    		if (j + k < m) plus(f[i + 1][j + 1][k], (LL)f[i][j][k] * (m - j - k));
    		if (j) plus(f[i + 1][j - 1][k + 1], (LL)f[i][j][k] * j);
    		if (j + k <= m - 2) plus(f[i + 1][j + 2][k], (LL)f[i][j][k] * (m - j - k) * (m - j - k - 1) / 2);
    		if (j >= 2) plus(f[i + 1][j - 2][k + 2], (LL)f[i][j][k] * j * (j - 1) / 2);
    		if (j + k < m && j) plus(f[i + 1][j][k + 1], (LL)f[i][j][k] * (m - j - k) * j);
    	}
    	int ans = 0;
    	for (int i = 0; i <= m; ++ i) for (int j = 0; j <= m; ++ j) plus(ans, f[n][i][j]);
    	printf("%d
    ", ans);
    }
    
  • 相关阅读:
    基于Haproxy+Keepalived构建高可用负载均衡集群
    基于 Haproxy 构建负载均衡集群
    shell for循环练习题99乘法表
    帮软件同事写的vsftpd服务虚拟用户管理脚本
    sed文件处理练习题
    判断ssh登录密码验证错误超过5次的IP被拉黑
    使用shell中数组功能生成自己的手机号
    利用Crontab设置每个月第一个周六的17:30执行/opt/shell.sh 脚本
    Tomcat 项目代码上线步骤详解
    Jar/War/Ear等包的作用与区别详解
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/7711898.html
Copyright © 2011-2022 走看看