• 洛谷 P2051 [AHOI2009]中国象棋


    题目描述

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

    输入输出格式

    输入格式:

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

    输出格式:

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

    输入输出样例

    输入样例#1: 复制
    1 3
    输出样例#1: 复制
    7

    说明

    样例说明

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

    数据范围

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

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

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

    题解

    30分暴力

    搜索即可

    50分状压

    题目说“N和M至少有一个数不超过8”故可以对“不超过8”的一个数状压

    因为炮横向和纵向的走法是一样的,所以直接讲数组旋转一下就行了

    100分普通dp

    正解并不需要用任何技术,只需要利用炮的一个性质:前面的炮只影响后面同一列的炮

    根据这一个性质,我们可以发现其实没有必要记录准确的状态,因为两个炮在哪一列并不重要,我们只要知道有几列有一个或两个炮即可。

    故可以设计状态:

    $f_{i,j,k}$存储方案数

    $i$表示操作到第$i$行

    $j$表示有$j$列有$1$个炮

    $k$表示有$k$列有$2$个炮

    初始化:

    $f_{0,0,0}=1$

    和状态转移:

    在原有1个炮的一列加1个炮

    在原有1个炮的两列各加1个炮

    在原来是空的一列加1个炮

    在原来是空的两列各加1个炮

    在原来是空的一列加1个炮,并在原有1个炮的一列加1个炮

    然后程序就很简单了,但需要注意边界

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long LL;
     4 const int MOD=9999973,MAXN=105;
     5 inline void add(LL &x,LL y){
     6     x=(x+y)%MOD;
     7 }
     8 int N,M;
     9 LL f[MAXN]/*line*/[MAXN]/*1*/[MAXN]/*2*/,ans;
    10 int main(){
    11     scanf("%d%d",&N,&M);
    12     
    13     f[0][0][0]=1;
    14     for(int i=1;i<=N;i++)
    15         for(int j=0;j<=M;j++)
    16             for(int k=0;j+k<=M;k++){
    17                 f[i][j][k]=f[i-1][j][k];/*do nothing*/
    18                 if(k)
    19                     add(f[i][j][k],f[i-1][j+1][k-1]*(j+1));/*add one to 1*/
    20                 if(k>1)/*add two to 1*/
    21                     add(f[i][j][k],f[i-1][j+2][k-2]*(j+2)*(j+1)/2);
    22                 if(j)/*add one to 0*/
    23                     add(f[i][j][k],f[i-1][j-1][k]*(M-j-k+1));
    24                 if(j>1)/*add two to 0*/
    25                     add(f[i][j][k],f[i-1][j-2][k]*(M-j-k+2)*(M-j-k+1)/2);
    26                 if(k&&j)/*add one to 0 and one to 1*/
    27                     add(f[i][j][k],f[i-1][j][k-1]*j*(M-j-k+1));
    28             }
    29     
    30     for(int i=0;i<=M;i++){
    31         for(int j=0;i+j<=M;j++){
    32             add(ans,f[N][i][j]);
    33         }
    34     }
    35     printf("%lld",ans);
    36     return 0;
    37 }
    View Code
  • 相关阅读:
    深度学习中的基本概念——梯度相关
    模型训练中涉及到的技巧
    ubuntu swapfile
    ubuntu install opencv
    Jetson TX1 安装ROS操作系统
    Redis介绍以及安装(Linux)
    C# url信息获取
    贝茨视觉训练法
    PHP设计模式浅析
    MySQL相关知识
  • 原文地址:https://www.cnblogs.com/guoshaoyang/p/11122313.html
Copyright © 2020-2023  润新知