【dp水题】HDU5079+LGP2051

HDU5079 Square

【题目大意】
给你一个 n×n(n8) 的棋盘,上面有一些格子必须是黑色,其它可以染黑或者染白,对于一个棋盘,定义它的优美度为它上面最大的连续白色子正方形的边长,对于每个0≤i≤n,问有多少种染色方案使得棋盘的优美度为i?

【解题思路】
比较显然的是这个题可以拆成n+1问来处理,每一问相当于求出优美度为i的方案数,但如果我们直接求恰好为i的方案数是比较困难的。考虑转化一下问题,我们就可以求出优美度至多为i的有多少种,这样答案显然就是求个差了。以下考虑我们现在要求的i为sz。

然后n最大才8,一眼看过去满满的状压味道,于是我们开始考虑状压。

一般的状压都是记这一个位置上有没有,但是这里不一样,这里对于解题有一个关键点——长度。然而长度最大也才8,我们可以考虑用一个数字将它记下。
我们发现,对于每一个格子,影响关于这个格子的最大正方形可以包括他的上方和包括它的右侧(有点乱,意思一下)。我们可以用 f[i][sta] 来表示DP到了第i行,该行状态为sta的方案数。 这里的sta显然不止是用01来表示的,我们可以把它看成一个n进制,这个sta的每一位代表 i i+sz1中每个格子能往上走的白色格子数的最小值。

这样问题变迎刃而解,因为我们发现它很容易转移。。。。。。
很显然DP方程为 f[i][newsta]=f[i1][sta] ,newsta为合法的情况(即去掉超过当前要求的边长的)。
然后乱搞就好了。

【参考代码】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN=10;
const int MAXM=1e5;
const int mod=1e9+7;
int n,m,T;
int f[MAXN][MAXM];
int cnb[MAXN],pws[MAXN][MAXN],ans[MAXN];//cnb=the state can not be 
int flag[MAXN];//in or not
char s[MAXN];

inline void get_pows()
{
    for(int i=2;i<MAXN-1;++i)
    {
        pws[i][0]=1;
        for(int j=1;j<MAXN-1;++j)
            pws[i][j]=pws[i][j-1]*i;
    }
}

inline void _reset()
{   
    m=1;
//  memset(f,0,sizeof(f));
    memset(ans,0,sizeof(ans));
    memset(cnb,0,sizeof(cnb));
}

inline void init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s);
        for(int j=0;j<n;++j)
        {
            if(s[j]=='o')
                m=(m<<1)%mod;
            else
                cnb[i]|=(1<<j);
        }
    }
}

inline void solve()
{
    ans[0]=1;ans[1]=(m-1+mod)%mod;

    for(int sz=2;sz<=n;++sz)//size
    {
        int k=n-sz+1,ms=pws[sz][k]; 
//      printf("ms:%d\n",ms);

        f[0][0]=1;
        for(int sta=1;sta<ms;++sta)
            f[0][sta]=0;
        for(int i=1;i<=n;++i)
        {
            for(int sta=0;sta<ms;++sta)
                f[i][sta]=0;
            for(int cur=0;cur<(1<<n);++cur)
            {
                if(cur&cnb[i])
                    continue;
//              printf("%d %d here\n",cur,cnb[i]);
                memset(flag,0,sizeof(flag));
                for(int j=0;j<n;++j)//line i,row j
                {
                    if(cur&(1<<j))
                        continue;
                    for(int q=0;q<k;++q)
                        if(j>=q && j<q+sz)
                            flag[q]=1;
                }
//              for(int q=0;q<k;++q)
//                  printf("%d ",flag[q]);
//              printf("\n");

                for(int sta=0;sta<ms;++sta)
                {
                    if(!f[i-1][sta])
                        continue;
                    int nsta=0;
//                  printf("%d\n",i);
//                  printf("here\n");
                    for(int j=0;j<k;++j)
                    {
                        int t=sta/pws[sz][j]%sz;//sz jingzhi,the jth num
//                      printf("t:%d\n",t);
                        if(flag[j])
                            t=0;
                        else
                        if(t!=sz-1)
                            ++t;
                        else
                        {
                            nsta=-1;
                            break;
                        }
                        nsta+=t*pws[sz][j];
//                      printf("new:%d\n",nsta);
                    }
                    if(nsta==-1)
                        continue;
                    f[i][nsta]=(f[i][nsta]+f[i-1][sta])%mod;
                }
            }

        }
//      printf("%d:\n",sz);
//      for(int i=0;i<ms;++i)
//          printf("%d\n",f[sz][i]);
//      printf("\n\n");

        for(int sta=0;sta<ms;++sta)
            ans[sz]=(ans[sz]+f[n][sta])%mod;
        ans[sz]=(m-ans[sz]+mod)%mod;
        ans[sz-1]=(ans[sz-1]-ans[sz]+mod)%mod;
    }
    for(int sz=0;sz<=n;++sz)
        printf("%d\n",ans[sz]);
}

int main()
{
    freopen("HDU5079.in","r",stdin);
    freopen("HDU5079.out","w",stdout);

    get_pows();
    scanf("%d",&T);
    while(T--)
    {
        _reset();
        init();
        solve();
    }

    return 0;
}

LGP2051 [AHOI2009]中国象棋

【题目描述】

这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!

【输入输出格式】

【输入格式】
一行包含两个整数N,M,之间由一个空格隔开。

【输出格式】
总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。

【输入样例】
1 3

【输出样例】
7

【数据范围】
100%的数据中N和M均不超过100
50%的数据中N和M至少有一个数不超过8
30%的数据中N和M均不超过6

【题目分析】
说实话,这题在LG上是紫色,但个人认为并没有到这个程度,因为太好想了。。。
我们发现,若我们对行进行dp,则做到当前行的方案数,只和之前有多少列放了多少个棋子有关,和具体怎么放实际上是无关的。显然我们可以考虑:
f[i][j][k] 为做到第i行,一共有j列放了1个棋子,有k列放了2个棋子的方案数。
然后胡乱转移就行了。

【参考代码】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;
const int mod=9999973;
const int MAXN=105;
int ans,n,m;
LL f[MAXN][MAXN][MAXN];//f[i][j][k]=now row i,have j rows 1chess,have k rows 2chess

int main()
{
    freopen("LGP2051.in","r",stdin);
    freopen("LGP2051.out","w",stdout);

    scanf("%d%d",&n,&m);
    f[1][0][0]=1;f[1][1][0]=m;f[1][2][0]=m*(m-1)/2;
    for(int i=2;i<=n;++i)
    {
        for(int j=0;j<=m;++j)
        {
            for(int k=0;k<=m;++k)
            {
                LL tot=0;
                tot+=f[i-1][j][k];//nothing
                tot+=f[i-1][j+1][k-1]*(j+1);//put 1 in 1
                tot+=f[i-1][j-1][k]*(m-j-k+1);//put 1 in 0
                tot+=f[i-1][j-2][k]*(m-j-k+2)*(m-j-k+1)/2;//put 2 in 0
                tot+=f[i-1][j+2][k-2]*(j+2)*(j+1)/2;//put 2 in 1
                tot+=f[i-1][j][k-1]*j*(m-j-k+1);//put 1 in 0,1 in 1
                f[i][j][k]=tot%mod;
            }
        }
    }
    for(int i=0;i<=m;++i)
        for(int j=0;j<=m;++j)
            ans=(ans+f[n][i][j])%mod;
    printf("%d\n",ans);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值