https://ac.nowcoder.com/acm/contest/330/D
C++版本一
std
题解:
状态压缩,轮廓线DP
好多6x6被骗去写暴搜的.jpg。实际上6x6全是?的答案高达1e18.
解法一:By 野鸡验题人
用 dp[x][state] 表示考虑到第 x 行,该行填写的数字的十进制状态为state的方案数。
这样的复杂度有点大,但是由于题目中的要求和为素数存在很强的限制,因此可以先预处理出所有符合条件的一行的state以及每个state可以转移的状态。加上本题宽松的时限,足以通过。
当然第一维是可以滚动掉的(然而并没有什么卵用)。
解法二:标程
用 dp[x][state] 表示考虑到第 x 个格子,它所在的轮廓线上所有数字的十进制状态为state的方案数。其中state的最高位存它前一行的那一格,最低为存它前一列的那一格。
如图所示,这样状态的转移就是一个“带溢出的十进制左移并补上填的数字“。
当然第一维是可以滚动掉的。
#include <bits/stdc++.h>
using namespace std;
char maze[10][10];
bool isprime[21] = {0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0};
long long dp[1000000 + 7][2];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++)
scanf("%s", maze[i]);
int top = 1;
for (int i = 1; i < m; i++)
top *= 10;
dp[0][0] = 1;
int v = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
for (int k = 0; k < top * 10; k++)
{
if (dp[k][v])
{
for (int add = 0; add < 10; add++)
{
if ((maze[i][j] == '?' || maze[i][j] == add + '0') && (!i || isprime[add + k / top]) && (!j || isprime[add + k % 10]))
{
dp[k % top * 10 + add][v ^ 1] += dp[k][v];
}
}
dp[k][v] = 0;
}
}
v ^= 1;
}
}
long long ans = 0;
for (int i = 0; i < top * 10; i++)
ans += dp[i][v];
printf("%lld\n", ans);
}