链接:
1444. 切披萨的方案数
题意:
给定一个矩阵,其中含有多个苹果,需要切割k-1次,每次可以切割多行/多列,需要保证切割两个部分都有苹果,移除靠上/靠右的部分,对留下部分进行后续的切割,求有几种切割方法
解:
男人不能快算法需要快!
这题需要通过两部分节约时间,一部分是动态规划,一部分是前缀和
这好像还是第一次写二维前缀和(好像),主要是要记得移除重复部分,由于每次保留的是靠下/靠左的部分,所以求的是已i j
为起点的右下角和
动态规划部分,我们需要一个三维的DP[k][i][j]
,分别代表从i j
开始的右下角矩阵,成功切割k-1刀的方案数量
初始值是DP[1][i][j]
=(sum[i][j] > 0)
,即这一整块上存在苹果
递推公式需要判断行切和列切,判断条件非常巧妙,是整体苹果数量 > 切割后剩下的苹果数量,公式是整体+=切割后的部分
整体DP[temp][i][j]
,假设在i2位置进行切割,则判断sum[i][j] > sum[i2][j]
,若为真即计算DP[temp][i][j]+=DP[temp-1][i2][j]
,要注意切割后所需的切割数-1
实际代码:
#include
using namespace std;
constexpr static int Mod=1E9+7;
int ways(vector& pizza, int k)
{
int lgrow=pizza.size(),lgcol=pizza[0].length();
vector>sum(lgrow+1,vector(lgcol+1));
vector>>dp(k+1,vector>(lgrow+1,vector(lgcol+1)));
//初始化
for(int i=lgrow-1;i>=0;i--)
{
for(int j=lgcol-1;j>=0;j--)
{
sum[i][j]=sum[i+1][j]+sum[i][j+1]-sum[i+1][j+1]+(pizza[i][j]=='A');//二维前缀和 矩阵计算
dp[1][i][j]=sum[i][j]>0;
}
}
//dp递推
for(int dp_k=2;dp_k<=k;dp_k++)
{
for(int i=lgrow-1;i>=0;i--)
{
for(int j=lgcol-1;j>=0;j--)
{
//行切割
for(int i2=lgrow-1;i2>i;i2--)
{
if(sum[i][j]>sum[i2][j])//切出来的有苹果
{
dp[dp_k][i][j]+=dp[dp_k-1][i2][j];
dp[dp_k][i][j]%=Mod;
}
}
//列切割
for(int j2=lgcol-1;j2>j;j2--)
{
if(sum[i][j]>sum[i][j2])//切出来的有苹果
{
dp[dp_k][i][j]+=dp[dp_k-1][i][j2];
dp[dp_k][i][j]%=Mod;
}
}
}
}
}
return dp[k][0][0];
}
int main()
{
int k;cin>>k;
vector pizza;string s;
while(cin>>s) pizza.push_back(s);
int ans=ways(pizza,k);
cout<
限制:
1 <= rows, cols <= 50
rows == pizza.length
cols == pizza[i].length
1 <= k <= 10
pizza 只包含字符 'A' 和 '.' 。