看了看某PPT后,乱写了一下午~~
下面的代码写法上不是最优的,但能体现思路,合写了的话就会根本不知道怎么来的:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=12;
const int NN=1<<N;
int n,m,b,s[N][N];
long long dp[N][N][NN];
bool One(int x,int p)
{
if (x&(1<<p)) return true;
else return false;
}
bool Zero(int x,int p)
{
if (x&(1<<p)) return false;
else return true;
}
int main()
{
int cases,casecnt=0;
scanf("%d",&cases);
while (cases--)
{
//input & init
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++) scanf("%d",&s[i][j]);
memset(dp,0,sizeof(dp));
for (int j=0; j<=m; j++) dp[0][j][0]=1;
b=(1<<(m+1))-1;
//DP
for (int i=1; i<=n; i++)
{
for (int k=0; k<=b>>1; k++)
dp[i][0][k<<1] = dp[i-1][m][k];
for (int j=1; j<=m; j++)
{
for (int k=0; k<=b; k++)
{
if (s[i][j]==0) // (i,j)位置上没有树
{
if (Zero(k,j-1) && Zero(k,j))
dp[i][j][k] += dp[i][j-1][k];
continue;
}
if (Zero(k,j-1) && Zero(k,j))
{
dp[i][j][k^(3<<(j-1))] += dp[i][j-1][k]; // 六种路径方向,3:30(时钟位置)
}
if (Zero(k,j-1) && One(k,j))
{
dp[i][j][k] += dp[i][j-1][k]; // 3:00
dp[i][j][k^(3<<(j-1))] += dp[i][j-1][k]; // 6:00
}
if (One(k,j-1) && Zero(k,j))
{
dp[i][j][k] +=dp[i][j-1][k]; // 6:45
dp[i][j][k^(3<<(j-1))] += dp[i][j-1][k]; // 9:15
}
if (One(k,j-1) && One(k,j))
{
dp[i][j][k^(3<<(j-1))] += dp[i][j-1][k]; // 9:00
}
}
}
}
if (n==1 || m==1) dp[n][m][0]=0;
printf("Case %d: There are %I64d ways to eat the trees.\n",++casecnt,dp[n][m][0]);
}
return 0;
}
这个DP思想重点(对于这题)在于考虑在整体之下的个体的根本规律,我们通过每格的应有姿态(回路中的点一进一出两个插头)来推算整体,又想到这种表达不难却很难想到的DP路径和状态,好厉害的说。继续学习。