随笔 - 58,  文章 - 0,  评论 - 4,  阅读 - 3296

一、题目描述:

  给你一个网格棋盘,a,b,c,d 表示了对应边长度,也就是对应格子数。

  例如,当 a=b=c=d=2 时,对应了下面这样一个棋盘:

          

  想要在这个棋盘上放 k 棋子,也就是这 k 个棋子没有两个在同一行,也没有两个在同一列,问有多少种方案。

  答案对 1e5+3 取模。数据保证 0<=a,b,c,d,k<=1e3,且至少有一种可行方案。


二、解题思路:

  棋子是一行一行、一列一列的攻击的,所以我们可以一列一列的 dp

  设 fi,j 表示前 i 列放 j 个棋子的方案数,si 表示第 i 列的方格数。

  容易得到状态转移方程:fi,j=fi1,j+fi1,j1×(si(j1))

 

  其实很好理解:

  第 i 列不放棋子的情况下,fi,j+=fi1,j。这种情况不用说了吧?

  第 i 列要放棋子的情况下,fi,j+=fi1,j1×i 列剩余位置数量。

  

  关于 第 i 列剩余位置数量:

  一列一列地来看,我们 dp 方程一列本来就最多放一个,不用管。

  一行一行地来看,fi1,j1 表示前 i1 行放了 j1 个棋子。

  一个棋子占据一行,其实就是第 i 列有 j1 行不能放了。

  那么还剩下 si(j1) 个位置,fi,j+=fi1,j1×(si(j1))

 

  但其实还有一个问题(困扰了我很久):

  如果 i1 列有一些行是第 i 列没有的怎么办?例如最上面的图。

  转移从左往右数的第 3 列时,第 2 列的方案里有一些是棋子放在了上面两行。

  这些棋子显然不会占据第 3 列的空位,但状态转移方程会 默认占用第 3 列的空位,导致方案数减少。

  

  怎么解决呢?

  显然只有 si1>si 才会出现这种问题,所以从右往左倒着转移就好了!

 

  时间复杂度 O((a+c)k),可以通过,而且很快。


三、完整代码:

复制代码
 1 #include<iostream>
 2 #define N 2010
 3 using namespace std;
 4 int a,b,c,d,k;
 5 int s[N],f[N][N];
 6 int main()
 7 {
 8     cin>>a>>b>>c>>d>>k;
 9     for(int i=1;i<=a;i++)    s[i+c]+=b;
10     for(int i=0;i<=a+c;i++)    s[i]+=d+1,f[i][0]=1;
11     for(int i=1;i<=a+c;i++)
12         for(int j=1;j<=k;j++)
13             f[i][j]=(f[i-1][j-1]*(s[i]-j)+f[i-1][j])%M;
14     cout<<f[a+c][k]<<'\n';
15     return 0;
16 }
复制代码

四、写题心得:

  好了,总算把这题想明白了,状态转移方程也是自己推的。很好,加油!

posted on   trh0630  阅读(58)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示