2014 鞍山区域赛 J HDU5079 Square DP套DP

题意:

给你一个 nn(n8) 的棋盘,上面有一些格子必须是黑色,其它可以染黑或者染白,对于一个棋盘,定义它的优美度为它上面最大的连续白色子正方形的边长,对于每个 0in ,问有多少种染色方案使得棋盘的优美度为 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/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值