题意
给定一个2行n列的矩阵,其中填好了一些数字(0-9),定义2*3的子矩阵为一个“六包“,现要用0-9填满这个2n的矩阵,要求每个六包中的数字的和都是K,求方案总数。
思路
暴力感觉很难写,DP应该是比较好下手
不难发现K≤54,列中的和≤18,每隔两列,列上的和相等。
处理每列的方案数
每列的填法数可能都不一样,那就干脆具体化,即把一个≤18的数a拆分成两个数的加法式,每项都要≤9,要分两种情况讨论(隔板法)
1.a≤9,就是a+1
2.a≥9,因为隔板到两边之间的球的数量都要≤9,即把两边不能放的位置减掉,即a-2*(a-9) +1
每一列有三种情况,即填入了0,1,2个
DP
1.设计状态
开始想两维,即第i列填k,然后方应该是列不出来了,因为i-1和i-2受限于前面的填法,不能直接乘,再for一层讨论i-1列,又要管i-5列,初始化开始就烦死了
要大致表示一个六包,给出两列填法才具体,也有了递推的那种连接感,方程一拍脑门就出来了
dp[i][j][k] = num[i][k] * dp[i - 1][p][j] ;
注意超范围跳过
初始化也好想,就是前两列的填法
然鹅为什么会WA#39!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//I
#include <bits/stdc++.h>
using namespace std;
#define ha 1000000007
const int maxn=3e5+4;
int n,k,num[maxn][19],a[maxn][3],K,nv;
int dp[maxn][19][19];
int main() {
cin >> n >> K >> nv;
if(K>54)K=54;
int ai,aj,v;
for (int i = 1; i <= nv; i++) {
scanf("%d%d%d", &aj, &ai, &v);
a[aj + 1][ai + 1] = v;
}
for (int i = 1; i <= n; i++) {
if (a[i][1] && a[i][2]) {
num[i][a[i][1] + a[i][2]] = 1;
continue;
} else if (!a[i][1] && !a[i][2]) {
for (int j = 0; j <=min(18,K); j++) {
if (j <= 9)
num[i][j] = j + 1;
else
num[i][j] = j - 2 * (j - 9) + 1;
//xxx|xxxxxx|xxx 中间才能划线
}
} else {
for (int j = max(a[i][1], a[i][2]); j <=min(K,9 + max(a[i][1], a[i][2])); j++) {
num[i][j] = 1;
}
}
}
//根据已经填了的数的个数分情况讨论
for (int j = 0; j <= min(18,K); j++)
for (int k = 0; k<=18&&j+k<=K; k++) {
dp[2][j][k] = num[1][j] * num[2][k];
}
long long ans = 0;
for (int i = 3; i <= n; i++)
for (int j = 0; j <= min(K,18); j++)
for (int k = 0; k <=min(K-j,18); k++) {
int p = K - j - k;
if(p<0||p>18)continue;
dp[i][j][k] = num[i][k] * dp[i - 1][p][j] % ha;
//前i列,第i列填k,i-1列填j的种数
//因为已经有数填上去了,所以不好暴力,如果只用两维,由于i-1填的值依据前面i-4填的值,不好初始化,也不能表示一整个六包的情况
}
for(int j=0;j<=min(18,K);j++)
for(int k=0;k<=K-j;k++)ans+=dp[n][k][j]%ha,ans%=ha;
cout<<ans;
}