题意:
给你一个 n⋅n(n≤8) 的棋盘,上面有一些格子必须是黑色,其它可以染黑或者染白,对于一个棋盘,定义它的优美度为它上面最大的连续白色子正方形的边长,对于每个 0≤i≤n ,问有多少种染色方案使得棋盘的优美度为 i ?
说起dp套dp,个人感觉并不是有多么难,关键一个是状态比较难设计
感觉往往内层dp是在给外层处理状态,帮助外层dp把状态压缩好,然后再用一般的dp套dp处理就行了。补了三道这样的题目。
HDU 5079 Square
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include<iostream>
#include <cmath>
#define xx first
#define yy second
#include <algorithm>
#include <map>
using namespace std;
const int N = 100010;
typedef long long ll;
const ll mod = 1e9+7;
int n;
ll a[10], ans[10], dp[9][1<<12];
char s[10];
ll f2[100];
// dp[i][st]表示搞到了第i行,状态为st
/*
st为一个k进制的数,存的是
这个数字中的第i位表示的是该行从第i个格子到第i+k-1个格子中每个格子往上走的连续白格子个数的最小值j。
这里注意的是,对于size为k的情况,我们只能通过dp维护出最大size<k的情况
然后通过求补求出最大size>=k的情况(因为这些j一旦有>=k的,只能知道他的最大size>=k)
但是却不能维护最大size>k的情况
处理完了这个st,只要枚举下一行的涂法,注意和地图本身一致,去掉错误的染色方法和j>=k的情况即可
个人感觉还可以预处理转移,不过不太会
*/
void init()
{
f2[0] = 1;
for( ll i = 1; i <= 65; i ++ ) f2[i] = f2[i-1]*2%mod;;
}
void solve()
{
scanf("%d", &n);
int i, k, cnt[10], j, sum = 0;
memset( ans, 0, sizeof(ans) );
ans[0] = 1;// all is black
for( i = 1; i <= n; i ++ ){
scanf("%s", s);
a[i] = 0;
for( j = 0; j < n; j ++ ){
if( s[j] == '*' )
a[i] |= ( 1 << j );
else sum ++;
}
}
ans[1] = ( f2[sum] - 1 + mod ) % mod;
for( int k = 2; k <= n; k ++ ){
int sizmax = 1;
//printf(" size: %d\n", k);
for( int t = 1; t <= n-k+1; t ++ ) sizmax *= k;
memset( dp, 0, sizeof(dp) );
dp[0][0] = 1;
for( int i = 1; i <= n; i ++ ){
// printf(" ceng : %d\n", i);
for( int st = 0; st < sizmax; st ++ ){
int tmp = st;
for( int s = 0; s < n-k+1; s ++ ){
cnt[s] = tmp%k;
tmp /= k;
}
for( int ran = 0; ran < ( 1 << n ); ran ++ ){ // 1 is white
int fl[10];
int sttmp = ( ((1<<n)-1) ^ ran );
if( ran & a[i] ) continue;
if( ( sttmp & a[i] ) != a[i] ) continue;
for( int j = 0; j < n; j ++ ){
if( ( ran & ( 1 << j ) ) == 0 ){
fl[j] = 1;
}// this is black
else fl[j] = 0;
}
int b = 0, f = 0, now[10];
for( int p = 0; p < n-k+1; p ++ ){
for( j = p; j < p+k; j ++ ){
if( fl[j] ){
now[p] = 0;
break;
}
}
if( j == p+k ) now[p] = cnt[p] + 1;//注意,这里处理新状态一定要新开数组,否则会互相影响
if( now[p] >= k ){
b = 1;
break;
}
}
if( b ) continue;
int newst = 0;
for( int p = 0, tt = 1; p < n-k+1; p ++, tt *= k ){ // ?
newst += now[p]*tt;
}
// printf("%3d %3d %3d %3d %3d %3d %3d", st, st%(k), (st/(k))%(k), ran, ran&1, (ran&2), (ran&4) );
// printf(" %3d %3d %3d \n", newst, now[0], now[1] );
dp[i][newst] += dp[i-1][st];
dp[i][newst] %= mod;
}
}
//for( int st = 0; st < sizmax; st ++ ){ printf("dp %d %d\n", st, dp[i][st]); }
}
for( int i = 0; i < sizmax; i ++ ){
ans[k] += dp[n][i];
ans[k] %= mod;
}
ans[k] = ( f2[sum] - ans[k] + mod ) % mod;
}
for( int i = 1; i <= n-1; i ++ ){
ans[i] = ( ans[i] - ans[i+1] + mod ) %mod;
}
for( i = 0; i <= n; i ++ )printf("%lld\n", ans[i]%mod);
}
int main()
{
int T;
// freopen("in.txt", "r", stdin);
cin >> T;
init();
while( T -- ){
solve();
}
}
还补了两道 HDU 4899 最大公共子序列的
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<vector>
#include<queue>
#include<map>
#define lson l, mid, x<<1
#define rson mid+1, r, x<<1|1
#define xx first
#define yy second
#define N 200000
using namespace std;
typedef __int64 ll;
const ll INF = 99999999999999999;
const ll mod = 1e9+7;
char s[20], num[4] = { 'A', 'C', 'G', 'T' };
int m, n;
ll f[2][(1<<15)+5];
int trans[(1<<15)+5][4];
ll ans[20];
void solve()
{
int i, j;
n = strlen(s);
int st = 0;
int dp[20], ndp[20];
for( st = 0; st < ( 1 << n ); st ++ ){
i = 1; dp[0] = 0;
memset( dp, 0, sizeof(dp) );
memset( ndp, 0, sizeof(ndp) );
for( i = 1; i <= n; i ++ ){
dp[i] = dp[i-1] + ( ( st >> (i-1) ) & 1 );
}
ndp[0] = 0;
for( j = 0; j < 4; j ++ ){
for( i = 0; i < n; i ++ ){
if( num[j] == s[i] ){
ndp[i+1] = dp[i] + 1;
}
else ndp[i+1] = max( ndp[i], dp[i+1] );
}
int newst = 0;
for( i = 1; i <= n; i ++ ){
newst |= ( ( ndp[i] != ndp[i-1] ) << (i-1) );
}
trans[st][j] = newst;
}
}
memset( f, 0, sizeof(f) );
f[0][0] = 1;
for( i = 0; i < m; i ++ ){
memset( f[(i&1)^1], 0, sizeof(f[(i&1)^1]) );
for( st = 0; st < ( 1 << n ); st ++ ){
for( j = 0; j < 4; j ++ ){
f[(i&1)^1][trans[st][j]] += f[i&1][st];
if( f[(i&1)^1][trans[st][j]] >= mod ) f[(i&1)^1][trans[st][j]] -= mod;
}
}
}
memset( ans, 0, sizeof(ans) );
for( st = 0; st < ( 1 << n ); st ++ ){
int cnt = 0;
int tmp = st;
while( st ){
cnt += ( st & 1 );
st >>= 1;
}
st = tmp;
ans[cnt] += f[m&1][st];
if( ans[cnt] >= mod ) ans[cnt] -= mod;
}
for( i = 0; i <= n; i ++ )printf("%I64d\n", ans[i]);
}
int main()
{
int T;
//freopen("in.txt", "r", stdin );
//freopen("out.txt", "w", stdout );
cin >> T;
while( T -- ){
scanf("%s%d", s, &m);
solve();
}
}
/*
观察到十分重要的一点 S和T的最长公共子序列,最大为lens
那么,我们想要得出内部的dp,即S每一位的增长状态(+1or no)
然后枚举添四个字母中的一个,来转移状态
预处理后,最后解决问题
*/
HDU 5548 南阳 麻将
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
int m;
const int mod = 1e9+7;
ll g[260][5];//g[id][l],id这个状态下取l个将会转化成什么样的状态
int dp[205][205][260];
bool can[260];
ll make_st( int op, int a, int b )
{
return op*25 + a*5 + b;
}
ll extend( int op, int a, int b, int j )
{
ll s = 0;
if( a == 0 ){
s |= 1LL << make_st( op, b, j );
if( j >= 3 ) s |= 1LL << make_st( op, b, j-3 );
if( j >= 2 &&!op ) s |= 1LL << make_st( 1, b, j-2 );
}
if( a >= 1 && a <= min( b, j ) ){
s |= 1LL << make_st( op, b-a, j-a );
if( !op && j-a >= 2 ) s |= 1LL << make_st( 1, b-a, j-a-2 );
if( j-a >= 3 ) s |= 1LL << make_st( op, b-a, j-a-3 );
}
return s;
}
//i-2剩下a张,i-1剩下b张,第i种牌选了j张,必须把a处理掉,否则以后就没有机会了
//没必要处理b,因为选b的时候一定会先处理的
void bfs()
{
queue<ll>q;
map<ll,int>mp;
m = 0;
q.push(1);
mp[1] = ++m;
while(!q.empty()){
ll tmp = q.front(); q.pop();
for( int j = 0; j <= 4; j ++ ){
ll s = 0;
for( int i = 0; i < 50; i ++ ){
if( (1LL<<i) & tmp ){
s |= (ll)extend( i/25, (i%25)/5, i%5, j );//搞清楚每个变量分别代表什么,s代表的是状态的集合
}//i是单纯的状态扩展
}
if( !mp[s] ){
mp[s] = ++m;
if( (1LL<<25) & s )can[m] = 1;
q.push(s);
}
g[mp[tmp]][j] = mp[s];
}
}
// for( int i = 1; i <= m; i ++ ){
// printf("g %d : ", i);
// for( int j = 0; j < 5; j ++ ){
// printf("%3d ", g[i][j]);
// }
// printf("\n");
// }
}
// 用bfs扩展出所有的状态,一共251个 = = 不过并不知道为什么只有这么一点
void solve()
{
dp[0][0][1] = 1;
for( int i = 0; i <= 200; i ++ ){
for( int j = 0; j <= 200; j ++ ){
for( int k = 1; k <= m; k ++ ){
if( dp[i][j][k] ){
for( int l = 0; l <= 4; l ++ ){
dp[i+1][j+l][g[k][l]] = ( dp[i+1][j+l][g[k][l]] + dp[i][j][k] ) % mod;
}
}
}
}
}
}
int main()
{
bfs();
solve();
//cout << m << endl;
int T, cas = 1;
cin >> T;
int p, q;
while( T -- ){
scanf("%d %d", &p, &q);
ll ans = 0;
for( int i = 1; i <= m; i ++ ){
if( can[i] ){
ans += dp[p][q][i];
ans %= mod;
}
}
printf("Case #%d: %I64d\n", cas++, ans);
}
}
最后给个信息: http://wjmzbmr.com/archives/my-favorite-problems-in-2014/