题意:
有一个 大小的棋盘,你需要在棋盘上面放
个点,问使得第一行,第一列,最后一行,最后一列都有点的方案数,答案对
取模。
解法一(暴力搜索,不可AC):
直接枚举矩阵每个点是否放点,然后判断是否符合要求再统计答案。
具体的,我们递归地用 表示当前考虑第
个点,当前已经放了
个点,依次统计答案。
代码:
class Solution {
public:
const int mod=1e9+7;
int n,m,k;
int ans;
int vis[4];//vis[0]表示第一行点的数量,vis[1]表示第一列点的数量,vis[2]表示最后一行点的数量,vis[3]表示最后一列点的数量
void dfs(int i,int j){
if(i>n*m){
if(j!=k)return;
//已经考虑完所有的点了
if(vis[0]&&vis[1]&&vis[2]&&vis[3])ans=(ans+1)%mod;
return;
}
if(j>k)return;//如果放的点比总的点数还要多,直接返回
//计算当前坐标
int x=(i-1)/m+1;
int y=i-(x-1)*m;
//放点
if(x==1)vis[0]++;
if(y==1)vis[1]++;
if(x==n)vis[2]++;
if(y==m)vis[3]++;
dfs(i+1,j+1);
if(x==1)vis[0]--;
if(y==1)vis[1]--;
if(x==n)vis[2]--;
if(y==m)vis[3]--;
dfs(i+1,j);
}
int solve(int n, int m, int k) {
this->n=n,this->m=m,this->k=k;
this->ans=0;
memset(vis,0,sizeof vis);
dfs(1,0);
return ans;
}
}; 时间复杂度: ,棋盘总共有
个点,每个点有两种方案,故时间复杂度为
空间复杂度: ,棋盘大小为
,需要递归的深度也为这么多,故空间复杂度为
解法二(状态压缩动态规划):
我们设 表示前
行,总共用了
个点,第一行,第一列,最后一行,最后一列是否有点的状态为
(
为一个4位的二进制数,第0、1、2、3位分别表示第一行,第一列,最后一行,最后一列,其中二进制位为1表示有点,为0表示没有点 )的方案数。
为了更直观起见,我们以下都用二进制串来表示 ,并且第0、1、2、3位是从左到右的。
首先考虑第1行:
第一行一个点也不放,显然方案数为1
第一行有点,第一列和最后一列都没有点,如下图所示:
在
个格子放
个点,
第一行、第一列有点,最后一列没有点,如下图所示:
在
个格子放
个点,
第一行、最后一列有点,第一列没有点,如下图所示:
在
个格子放
个点,
第一行、第一列,最后一列都有点,如下图所示:
在
个格子放
个点,
接着我们考虑第
行:
我们考虑枚举状态
1. 当
表示的第一列,最后一列都为1时:
分为三种情况:
(1)当前行的上面第一列、最后一列都没有点,如下图所示:
那么我们考虑当前行总共放
个点,第一列和最后一列必须各放一个点,在
个格子放
个点,即:
(2)当前行上面第一列没有点、最后一列有点,如下图所示:
那么我们考虑当前行总共放
个点,我们当前行第一列一定要放一个点,剩下的位置随便放,在
个格子放
个点,即:
(2)同理,当前行上面最后一列没有点,第一列有点,可得:
综上所述,当
类似的,我们可以得出下面几种情况。
2. 当3. 当s表示的第一列为0,最后一列为1时:
4. 当s表示的第一列,最后一列都为0时:
由于最后一行状态我们只需要答案表示的状态,故我们直接考虑答案如何求解:
代码:
class Solution {
public:
const int mod=1e9+7;
int f[31][1001][1<<4];
int C[31][31];//杨辉三角,C[i][j]表示组合数
inline int calc(int n,int m){//计算组合数
if(n<0||m<0)return 0;
if(m>n)return 0;
return C[n][m];
}
inline int get_state(bool a,bool b,bool c,bool d){//第0、1、2、3位是否是1的状态
int x=0;
if(a)x|=1;
if(b)x|=1<<1;
if(c)x|=1<<2;
if(d)x|=1<<3;
return x;
}
inline bool check_state(int x,int k){//判断状态x的第k位是否是1
return x&(1<<k);
}
int solve(int n, int m, int k) {
memset(f,0,sizeof f);
memset(C,0,sizeof C);//多测清空
C[0][0]=1;
for(int i=1;i<=30;i++){
C[i][0]=C[i-1][0];
for(int j=1;j<=i;j++){
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;//递推求解杨辉三角
}
}
f[1][0][0]=1;//第一行,放了0个点,第一行,第一列,最后一行,最后一列都没有点的方案数为1
for(int j=1;j<=k;j++){
//设置第一行边界状态
f[1][j][get_state(true,false,false,false)]=calc(m-2,j);
f[1][j][get_state(true,true,false,false)]=calc(m-2,j-1);
f[1][j][get_state(true,false,false,true)]=calc(m-2,j-1);
f[1][j][get_state(true,true,false,true)]=calc(m-2,j-2);
}
for(int i=2;i<n;i++){
//第2~n-1行状态
for(int j=0;j<=k;j++){
for(int c=0;c<=j&&c<=m;c++){
for(int s=0;s<(1<<4);s++){
if(check_state(s,2))continue;//第2到n-1行所表示的状态s最后一行肯定没有点
if(check_state(s,1)&&check_state(s,3)){
//第一列,最后一列的点都唯一在当前行
f[i][j][s]+=1ll*f[i-1][j-c][s^(1<<1)^(1<<3)]*calc(m-2,c-2)%mod;
f[i][j][s]%=mod;
//上面没有第一列的点,那么当前行在第一列一定要放点
f[i][j][s]+=1ll*f[i-1][j-c][s^(1<<1)]*calc(m-1,c-1)%mod;
f[i][j][s]%=mod;
//上面没有最后一列的点,那么当前行最后一列一定要放点
f[i][j][s]+=1ll*f[i-1][j-c][s^(1<<3)]*calc(m-1,c-1)%mod;
f[i][j][s]%=mod;
//上面第一列和最后一列都有点,这一行随便放
f[i][j][s]+=1ll*f[i-1][j-c][s]*calc(m,c)%mod;
f[i][j][s]%=mod;
}else if(check_state(s,1)){
//上面没有第一列的点,那么当前行所在的第一列一定要放点
f[i][j][s]+=1ll*f[i-1][j-c][s^(1<<1)]*calc(m-2,c-1)%mod;
f[i][j][s]%=mod;
//上面有第一列的点,那么随便放
f[i][j][s]+=1ll*f[i-1][j-c][s]*calc(m-1,c)%mod;
f[i][j][s]%=mod;
}else if(check_state(s,3)){
//上面没有最后一列的点,那么当前行所在的最后一列一定要放点
f[i][j][s]+=1ll*f[i-1][j-c][s^(1<<3)]*calc(m-2,c-1)%mod;
f[i][j][s]%=mod;
//上面有最后一列的点,那么随便放
f[i][j][s]+=1ll*f[i-1][j-c][s]*calc(m-1,c)%mod;
f[i][j][s]%=mod;
}else{
//第一列和最后一列都没有,那么在m-2范围内随便放
f[i][j][s]+=1ll*f[i-1][j-c][s]*calc(m-2,c)%mod;
f[i][j][s]%=mod;
}
}
}
}
}
int s=get_state(true,true,true,true);
int ans=0;
for(int j=1;j<k;j++){
//按照上面同理进行讨论
ans+=1ll*f[n-1][k-j][s^(1<<1)^(1<<3)^(1<<2)]*calc(m-2,j-2)%mod;
ans%=mod;
ans+=1ll*f[n-1][k-j][s^(1<<1)^(1<<2)]*calc(m-1,j-1)%mod;
ans%=mod;
ans+=1ll*f[n-1][k-j][s^(1<<3)^(1<<2)]*calc(m-1,j-1)%mod;
ans%=mod;
ans+=1ll*f[n-1][k-j][s^(1<<2)]*calc(m,j)%mod;
ans%=mod;
}
return ans;
}
}; 时间复杂度:,
数组最后一维的大小为
,可视为常数,故状态
的复杂度为
的,每次转移需要枚举当前行选了多少个,代价是
的,故总的时间复杂度为
空间复杂度: ,存储状态
需要
级别的内存,存储杨辉三角需要
级别的内存,故总的空间复杂度为

京公网安备 11010502036488号