题目
分析
显而易见的暴力做法是,枚举每一条路径,把经过的每一单位的值边乘边取模。每个点可以选择向右或者向下两个方向,所复杂度(
2n
2
n
)。
如果使用动态规划,判断某个位置的棋子能否是p来进行转移,得到(n,m)的所有可能。显然可以设f[i][j][p]表示位置(i,j)可否为k。边界条件就是
f[1][1][a[1][1]mod k]=true
f
[
1
]
[
1
]
[
a
[
1
]
[
1
]
m
o
d
k
]
=
t
r
u
e
。因为每一步都需要取模,否则结果会大的无法想象,所以利用填表法很难在转移时记录上一个格子中取模之前的数。那么不妨使用刷表法,无需记录上一步的原始值。方程很好理解:
代码
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=102;
int a[maxn][maxn],ans[maxn];
bool f[maxn][maxn][maxn];
int main()
{
int n,m,k,cnt=0;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
a[i][j]%=k;
// f[i][j][a[i][j]]=1;
//一开始没有仔细思考,以为每个格子的初始值都可能出现在这个格子的棋子可能值中=_=
}
f[1][1][a[1][1]]=1;//其实只有第一步时初始为1,才可能出现这种可能值。
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int p=0;p<k;p++)
if(f[i][j][p])
{
f[i+1][j][(p*a[i+1][j])%k]=1;
f[i][j+1][(p*a[i][j+1])%k]=1;
}
for(int i=0;i<k;i++)
if(f[n][m][i])
ans[++cnt]=i;
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)
printf("%d ",ans[i]);
return 0;
}