【狀壓DP】【cofun1373】中國象棋(cchess)

【cofun1373】中國象棋(cchess)

Description
在N行M列的棋盤上,放若干個炮(可以是0個),使得沒有任何一個炮可以攻擊另一個炮。 請問有多少种放置方法,中國象棋中炮的行走方式大家應該很清楚吧.

Input Format
兩個整數n,m
Output Format
方案總數 mod 9999973

Sample Input
1 3
Sample Output
7

Hint
【數據規模】30%的數據 n,m<=6
50%的數據n,m中至少有一個不超過8
100%的數據n,m<=100
雖然有取模,仍建議使用int64


  • 分析:
    • 對於50%的數據:n,m 中至少有一個不超過8
      可以考慮狀壓,把情況dfs出來再DP,類似炮兵陣地。
    • 對於100%的數據:類似組合數學的方法【網上看的。。but我覺得更像是普通的DP 2333】
      題目很坑 ,“中國象棋中炮的行走方式大家應該很清楚吧”。。然鵝肯定有人不清楚【比如我SZO】,科普來了~23333, 反正最終會get到:每一行每一列最多隻能有兩隻棋子。於是推出了→
      *轉移方程:
 f[i][j][k] = f[i - 1][j][k];//不放棋子
 if (j)
     f[i][j][k] += f[i - 1][j - 1][k] * (m - j + 1 - k);//在一個原來的空列上添加一隻棋子
 if (k && j + 1 <= m)
     f[i][j][k] += f[i - 1][j + 1][k - 1] * (j + 1);//把一個原有一隻棋子的列變爲有兩隻棋子的列
 if (j > 1)
     f[i][j][k] += f[i - 1][j - 2][k] * ((m - j + 2 - k) * (m - j + 1 - k) / 2);//在兩個原來的空列上分別添加一隻棋子
 if (k > 1 && j + 2 <= m)
     f[i][j][k] += f[i - 1][j + 2][k - 2] * ((j + 2) * (j + 1) / 2);//把兩個原有一隻棋子的列分別變爲有兩隻棋子的列
 if (j && k)
     f[i][j][k] += f[i - 1][j][k - 1] * j * (m - j - k + 1);//在一個原來的空列上添加一隻棋子,並把一個原有一隻棋子的列變爲有兩隻棋子的列

f[i][j][k]: 到i行爲止,有j列放一隻棋子,k列放兩隻棋子。

【雖然有點麻煩但是好理解~有個問題 爲啥在兩個空列上分別添加不等下一回DP而要先轉移呢?明天去問同學~【莫名Flag

  • 媽耶我可能傻了。。因爲是枚舉到該行,所以放兩個列是在該行放兩個,放一個列是在該行放一個,如果等到下一回循環,那就是在下一行放,這是不一樣的。
    好尷尬-_-||問了一個神犇,可是每次都是他回覆前我就突然開竅了。。QAQ

  • 代碼:
 #include <bits/stdc++.h>
 using namespace std;

 const long long MO = 9999973;

 int n, m, i, j, k;
 long long f[105][105][105], ans;

 inline int read()
 {
    int x = 0, w = 1;
    char ch = 0;
    while(ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
     }
    while(ch >= '0' && ch <= '9')
          x = x * 10 + ch - '0', ch = getchar();
    return x * w;
  } //讀入優化

 inline void write(long long x)
 {
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
 }//輸出優化

 int main()
 {

    n = read(), m = read();
    //讀入【本po代碼補全設了讀入優化,懶得刪了~見諒=w=
    memset(f, 0, sizeof(f));
    f[0][0][0] = 1;
    for(i = 1; i <= n; i ++)
        for(j = 0; j <= m; j ++)
            for(k = 0; k + j <= m; k ++)
            {
                f[i][j][k] = f[i - 1][j][k];
                if (j)
                    f[i][j][k] += f[i - 1][j - 1][k] * (m - j + 1 - k);
                if (k && j + 1 <= m)
                    f[i][j][k] += f[i - 1][j + 1][k - 1] * (j + 1);
                if (j > 1)
                    f[i][j][k] += f[i - 1][j - 2][k] * ((m - j + 2 - k) * (m - j + 1 - k) / 2);
                if (k > 1 && j + 2 <= m)
                    f[i][j][k] += f[i - 1][j + 2][k - 2] * ((j + 2) * (j + 1) / 2);
                if (j && k)
                    f[i][j][k] += f[i - 1][j][k - 1] * j * (m - j - k + 1);
                f[i][j][k] %= MO; 
             }
    //狀態轉移      
    for(i = 0; i <= m; i ++)
        for(j = 0; j + i <= m; j ++)
            ans = (ans + f[n][i][j]) % MO;
    write(ans);
    //統計答案並輸出【輸出優化原因同上,不過真的蠻省時的QUQ
    return 0;

 }

碼了兩回qwq剛上手沒有及時保存的好習慣ORZ閃退了TAT
睡覺啦~晚安安。O(∩_∩)O

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章