AHOI2009 中国象棋
題目鏈接
題目描述
在一個N行M列的棋盤上,讓你放若干個炮(可以是0個),使得沒有一個炮可以攻擊到另一個炮,請問有多少種放置方法。
(在中國象棋中炮的行走方式是:一個炮攻擊到另一個炮,當且僅當它們在同一行或同一列中,且它們之間恰好 有一個棋子。)
輸入輸出格式
輸入格式:
一行包含兩個整數N,M,之間由一個空格隔開。
輸出格式:
總共的方案數,由于該值可能很大,只需給出方案數模9999973的結果。
樣例
| 1 3 | 7 |
思路
1. 30分做法
dfs,復雜度玄學
沒有代碼2.50分做法
分析題目
當滿足題意下,每行每列有且最多只有兩個炮
注意題目保證行或列小于等于8
考慮dp 按行或列轉移(假設按行轉移,即n<=8)
如果知道了考慮到當前行時,每列已經放了的棋子個數
那么當前行放棋子的方案就能夠確定(每行最多放兩個)
那么如果會狀壓的同學應該已經會了 (逃
定義dp[n][4^m]表示當考慮到第n行時,之前所放的棋子狀態為2^2m時的方案數
通過兩位二進制表示一列的狀態 00代表沒有放棋子 01代表放了一個棋子 11代表不能再放棋子
然后轉移就可以了
3.滿分做法
考慮dp轉移過程
- 不選
- 選00->01
- 選01->11
- 選00,00->01,01
- 選00,01->01,11
- 選01,01->11,11
如果你發現,轉移并不用知道每一列究竟放了幾個棋子的話...
定義dp[n][i][j]表示當考慮到第n行時,沒有放棋子的有i列,放了一個棋子的有j列
定義C(x,y)為在x中取y個的方案數
那么上面的轉移過程表示出來就是
- C(i+j,0)
- C(i,1)
- C(j,1)
- C(i,2)
- C(i,1)*C(j,1)
- C(j,2)
假設現在取了 00,01
那么就有 dp[n+1][i-1][j-1]=dp[n][i][j]*C(i,1)*C(j,1)
有代碼
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define rint register int #define ci const int& #define ll long long using namespace std;const int mod=9999973; ll dp[101][102][102]; int n,m; ll ans=0;int main(){cin>>n>>m;//初始化,在第0行,放了1個的列為0(即01為0),放了0個的列為m(即00為m)的情況數有一種 dp[0][0][m]=1;for(rint i=0;i<n;i++)for(rint j=0;j<=m;j++)for(rint k=0;k<=m-j;k++)if(dp[i][j][k]){//如果狀態可到達 if(k){//當00數>=1時 if(j)dp[i+1][j][k-1]+=dp[i][j][k]*k*j,dp[i+1][j][k-1]%=mod;//當01數>=1時,選00,01放置 dp[i+1][j+1][k-1]+=dp[i][j][k]*k;dp[i+1][j+1][k-1]%=mod;//選00放置 if(k>1)dp[i+1][j+2][k-2]+=dp[i][j][k]*k*(k-1)/2,dp[i+1][j+2][k-2]%=mod;//當00數>=2時,選00,00放置 }if(j){//當01數>=1時 dp[i+1][j-1][k]+=dp[i][j][k]*j;dp[i+1][j-1][k]%=mod;//選01放置 if(j>1)dp[i+1][j-2][k]+=dp[i][j][k]*j*(j-1)/2,dp[i+1][j-2][k]%=mod;//當01數>=2時,選01,01放置 }dp[i+1][j][k]+=dp[i][j][k];dp[i+1][j][k]%=mod;//不放 }for(rint j=0;j<=m;j++)for(rint k=0;k<=m-j;k++)if(dp[n][j][k])(ans+=dp[n][j][k])%=mod;//統計 cout<<ans;//dp數組用long long,中間存在乘法,極限情況下會爆int,親身試驗.... }轉載于:https://www.cnblogs.com/ullio/p/9369323.html
總結
以上是生活随笔為你收集整理的AHOI2009 中国象棋的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 自定义ListView单
- 下一篇: 记一次因坏块引起的dataguard恢复