状态压缩的dp问题
先看一段超时的代码。。。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <cmath>
using namespace std;
char tpcha[500];
int data[500][500];
int dp[2][500][11][11][2];
const int MOD = 1000000007;
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
int t, cs, i, j, a, b;
int n, m;
int tp;
cin>>t;
cs = 0;
while (t--)
{
memset(dp, 0, sizeof dp);
printf("Case %d: ", ++cs);
cin>>n>>m;
for ( i = 1; i<= n; i++)
{
cin>>tpcha;
for (j = 0; j< m; j++)
{
data[i][j+1] = tpcha[j]-'0';
}
}
int now, bef;
int cao = 0;
for (i = 1; i<= n; i++)
{
now = i%2;
bef = (i+1)%2;
for (j = 1; j<= m; j++)
{
for (a = 0; a<= 10; a++)
{
for (b = 0; b<= 10; b++)
{
dp[now][j][a][b][1] = dp[now][j][a][b][0] = 0;
tp = data[i][j];
if (b >= tp)
dp[now][j][a][b][1] += dp[bef][j][a][b-tp][0],dp[now][j][a][b][1] += dp[now][j-1][a][b-tp][0];
else
dp[now][j][a][b][1] += dp[bef][j][a][11+b-tp][0], dp[now][j][a][b][1] += dp[now][j-1][a][11+b-tp][0];
if (a >= tp)
dp[now][j][a][b][0] += dp[bef][j][a-tp][b][1], dp[now][j][a][b][0] += dp[now][j-1][a-tp][b][1];
else
dp[now][j][a][b][0] += dp[bef][j][11+a-tp][b][1], dp[now][j][a][b][0] += dp[now][j-1][11+a-tp][b][1];
if (dp[now][j][a][b][0] > MOD)
dp[now][j][a][b][0] %= MOD;
if (dp[now][j][a][b][1] > MOD)
dp[now][j][a][b][1] %= MOD;
}
}
if (i > 1)
dp[now][j][data[i-1][j]][data[i][j]][1]++;
if (j > 1)
dp[now][j][data[i][j-1]][data[i][j]][1]++;
for (a = 0; a<= 10; a++)
{
cao += dp[now][j][a][a][0]+dp[now][j][a][a][1];
if (cao > MOD)
cao %= MOD;
}
}
}
printf("%d\n", cao);
}
return 0;
}
在这里用了最直观的的 dp[2][500][11][11][2],dp[滚动数组(行)][列][人的能量][剑的能量][当前加能量的对象 0人 1剑]
时间为 n*m*11*11,果断超了
后来想到所限制的能量范围0-10是一个循环,在这个循环中可以把人和剑的能量关系压缩为两者的差值
那么状态转移时,将上一差值取反加上当前的能量就是新的差值
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <cmath>
using namespace std;
const int MAXN = 500;
const int MOD = 1e9+7;
int dp[MAXN][MAXN][11];
char tpchar[MAXN];
int valu[MAXN][MAXN];
inline mabs(int a)
{
return a>0?a:-a;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
int i, j;
int t, cs = 0;
int n, m;
cin>>t;
while (t--)
{
printf("Case %d: ", ++cs);
cin>>n>>m;
for (i = 0; i< n; i++)
{
cin>>tpchar;
for ( j = 0; j< m; j++)
valu[i][j] = tpchar[j]-'0';
}
memset(dp, 0, sizeof dp);
int cnt = 0;
int a, b, c, d;
for (i = 0; i< n; i++)
{
for (j = 0; j< m; j++)
{
for ( int k = 0; k< 11; k++)
{
a = (11-k)%11;
b = (a+valu[i+1][j])%11;
dp[i+1][j][b] += dp[i][j][k];
dp[i+1][j][b]%=MOD;
b = (a+valu[i][j+1])%11;
dp[i][j+1][b] += dp[i][j][k];
dp[i][j+1][b]%=MOD;
}
a = valu[i][j]-valu[i+1][j];// 1
dp[i+1][j][(11-a)%11]++;
a = valu[i][j]-valu[i][j+1];// 2
dp[i][j+1][(11-a)%11]++;
cnt += dp[i][j][0];
cnt %= MOD;
}
}
cout<<cnt<<endl;
}
return 0;
}
其中1 和 2 处要注意,新加入一条路径时,要把能量差取反,使整个状态转移保持一致