P2051 [AHOI2009]中国象棋[线性DP]

最近智商有点不在线。其实一直不在线。

题目



先是想用$f[i][j][k][0/1/2]$表示摆了i行时有j列空着,k列有了一个炮,且当下摆了0/1/2个的状态,转移方程写的出来但是极其繁琐。于是又设法听取评讲者题解修改状态,最后的012完全可以删去。那么仍可以表示这一行那些列摆过1个,那些列摆过0个的种类。转移时分类即可。

$f[i][j][k]+=f[i-1][j][k]$ 什么都不摆

$f[i][j][k]+=(j+1)*f[i-1][j+1][k-1]$  摆1个炮

$f[i][j][k]+=(k+1)*f[i-1][j][k+1]$  摆1个炮

$f[i][j][k]+=(j+1)*(j+2)/2*f[i-1][j+2][k-2]$  摆两个炮,下同

$f[i][j][k]+=(k+1)*(k+2)/2*f[i-1][j][k+2]$

$f[i][j][k]+=k*(j+1)*f[i-1][j+1][k]$

注意边界就行。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 typedef pair<int,int> pii;
 5 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
 6 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
 7 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
 8 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
 9 template<typename T>inline T read(T&x){
10     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c==‘-‘)f=1;
11     while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();return f?x=-x:x;
12 }
13 const int N=100+7,P=9999973;
14 int n,m,ans,f[N][N][N];
15
16 inline void inc(int&x,int y){(x+=y)>=P?x-=P:1;}
17
18 int main(){//freopen("tmp.in","r",stdin);freopen("tmp.out","w",stdout);
19     read(n),read(m);if(m<2){printf("%d\n",1+n+(n)*(n-1)/2);return 0;}
20     f[1][m][0]=1,f[1][m-1][1]=m,f[1][m-2][2]=m*(m-1)/2;
21     for(register int i=2;i<=n;++i)
22         for(register int j=0;j<=m;++j)
23             for(register int k=0;k<=m-j;++k){
24                 inc(f[i][j][k],f[i-1][j][k]);
25                 if(k)inc(f[i][j][k],(j+1)*f[i-1][j+1][k-1]%P);
26                 inc(f[i][j][k],(k+1)*f[i-1][j][k+1]%P);
27                 if(k>=2)inc(f[i][j][k],((j+1)*(j+2)>>1)*1ll*f[i-1][j+2][k-2]%P);
28                 inc(f[i][j][k],((k+1)*(k+2)>>1)*1ll*f[i-1][j][k+2]%P);
29                 inc(f[i][j][k],1ll*k*(j+1)*f[i-1][j+1][k]%P);
30             }
31     for(register int i=0;i<=m;++i)for(register int j=0;j<=m-i;++j)inc(ans,f[n][i][j]);
32     printf("%d\n",ans);
33     return 0;
34 }

原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/10358509.html

时间: 2024-08-14 04:15:18

P2051 [AHOI2009]中国象棋[线性DP]的相关文章

Luogu P2051[AHOI2009]中国象棋【dp】By cellur925

题目传送门 题目大意:给定一个$n*m$的棋盘,求放三个"炮"使它们不共行也不共列的方案数.($n,m$$<=100$) 这题主要是转移比较困难,因为情况比较多,所以需要冷静大胆细心地进行分情况讨论. 首先我们还是设计出状态:设$f[i][j][k]$表示前$i$行,放1枚棋子的有$j$列,放2枚棋子的有$k$列的方案数. 我们这样思考:放几个?放在哪? 在第$i$行不放棋子.显然我们可以由$f[i-1][j][k]$转移过来. (f[i][j][k]+=f[i-1][j][k]

Luogu P2051 [AHOI2009]中国象棋(dp)

P2051 [AHOI2009]中国象棋 题面 题目描述 这次小可可想解决的难题和中国象棋有关,在一个 \(N\) 行 \(M\) 列的棋盘上,让你放若干个炮(可以是 \(0\) 个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法.大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子.你也来和小可可一起锻炼一下思维吧! 输入输出格式 输入格式: 一行包含两个整数 \(N\) , \(M\) ,之间由一个空格隔开.

P2051 [AHOI2009]中国象棋

题目描述 这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法.大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子.你也来和小可可一起锻炼一下思维吧! 输入输出格式 输入格式: 一行包含两个整数N,M,之间由一个空格隔开. 输出格式: 总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果. 输入输出样例 输入样例#

p2051 [AHOI2009]中国象棋. (bzoj 1801)

题目描述 这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法.大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子.你也来和小可可一起锻炼一下思维吧! 40pts 考试遇到了这个题,玄学打表得了\(40pts\) 玄学打表吼啊 xjb分析 正解竟然是个\(DP\)? 还有人说是状压\(DP\)?哪里来的状压啊! 前置知识 考虑到我

luogu P2051 [AHOI2009]中国象棋

分析: 因为这是道dp题n<=100,第一想到O(n^3)的算法,发现每行每列最多两个炮,f(i,j,k)表示放完第i行,有j列一个炮,与k列两个炮的方案.即可递推. 详细看代码. #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const ll MOD=9999973; const int maxn=110; ll f[maxn][maxn][maxn]; /

[luoguP2051] [AHOI2009]中国象棋(DP)

传送门 注释写明了一切 #include <cstdio> #define N 111 #define p 9999973 #define LL long long int n, m; LL ans, f[N][N][N]; //每一行和每一列可以放0/1/2个炮 //f[i][j][k]表示前i行放了1个炮的列有j个,放了2个炮的列有k个的方案数 //那么可以推出,放了0个炮的列有m-j-k个 inline int C(int x) { return x * (x - 1) / 2; } i

BZOJ 1801 中国象棋(DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1801 题意:在n*m的棋盘上放若干炮使得不互相攻击.有多少种放法?可以放0个.1个....只要不互相攻击就行.. 思路:f[i][j][k]前i行j列有1个炮.k列有两个炮. int n,m; i64 f[N][N][N]; void up(i64 &x,i64 y) { x+=y; x%=mod; } i64 C(int x) { return x*(x-1)/2; } int ma

BZOJ1801 [Ahoi2009]chess 中国象棋(DP, 计数)

题目链接 [Ahoi2009]chess 中国象棋 设f[i][j][k]为前i行,j列放了1个棋子,k列放了2个棋子的方案数 分6种情况讨论,依次状态转移. 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for (int i(a); i <= (b); ++i) 6 7 typedef long long LL; 8 const LL mod = 9999973; 9 int n

BZOJ1801:[AHOI2009]中国象棋——题解

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