状态压缩dp(hdu2662)(我综合了一个人的解释和另一个人的代码)

hoj 2662


大概题意是:有一个n*m的棋盘,在这个棋盘里边放k个旗子,要求每一行每一列都不能存在一对旗子相邻,问最后总共的方案数。


这道题一看状态非常多,就一定是状压。怎么状压呢?这又是个问题。
慢慢考虑下这道题每个局面分别有哪些状态,我们很容易就能想到有以下几个状态:
第一:每一行放了多少个旗子;
第二:已经用了多少个旗子;
第三:已经放的这些旗子能不能保证合法,即上下左右均不相邻。
在这里可能还不容易想到那个状态表达式,那么我们先来考虑个简单的,假如说只有一行,要求在这一行里边填充k个旗子,要求任意两个都不相邻,这个时候的dp应该怎么表示?这就很简单了,直接就是dp[i][j][x],代表已经到了第i列,已经使用了j个旗子,而且当前第i列的状态就是x(当然这里x只能是0和1,这里0代表这个第i列没有放旗子,1就代表这个位置放了旗子)的总方案数,递推关系是怎么写?其实也很简单,
dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1];
dp[i][j][1]=dp[i-1][j-1][0];//这里只能是dp[i-1][j-1][0],因为第i列已经放了,那么第i-1列就一定不能放。
当然这里你需要考虑到二维的局面,怎么考虑,把行对应于列,每一列的状态转化为每一行的状态,前i列使用了j个旗子变成前i行使用了j个旗子就这样思考。
综上考虑,我们会想到要有一个这样的dp,就是dp[i][j][x],这里代表的是:
填充旗子已经填到第i行了,已经使用了j个旗子,而且当前第i行的状态就是x的这么一个
表示前i行的总方案数。
那么递推怎么推?
dp[i][j][x]+=dp[i-1][j-num(mark[x])][y];
解释一下,这里的x是当前第i行的状态,而这个mark[x]代表当前状态下的十进制表示,也就是说把一个状态表示成十进制之后就是mark[x]了,这里为什么是j-num(mark[x])呢?因为啊,你这样想。反过来推。如果你在前i-1行已经使用了j-num(mark[x])个旗子,而且num(mark[x])就代表第i行你使用的旗子,那么你在前i行是不是就使用了j个旗子?
就这样逆推。这个mark[x]和mark[y]分那别代表第i行和第i-1行的状态的十进制表示。
最后你只要把dp[n][k][i]其中i是从0到最多状态的那些状态,把他们加起来。
#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<string>  
#include<cmath>  
#include<algorithm>  
#include<cstdlib>  
  
using namespace std;  
  
long long n,m,k,dp[82][22][1<<9],ans,mm;//dp[i][j][x]第i行放了j个棋子当前状态为x时的方法数  
long long mark[1<<9],len;//十进制标记每一行的状态  
  
using namespace std;  
  
long long num(long long x) //记录状态x中1的个数  
{  
    long long sum=0;  
    while(x)  
    {  
        if(x&1)  
            sum++;  
        x=x>>1;  
    }  
    return sum;  
}  
  
bool judge(long long x) //判断状态x是否有相邻的棋子放在一起  
{  
  
    if(x&(x<<1)) return false;  
    return true;  
}  
  
int main()  
{  
    while(scanf("%lld %lld %lld",&n,&m,&k)!=EOF)  
    {  
           ans=0;  
           memset(dp,0,sizeof(dp));  
           memset(mark,0,sizeof(mark));  
           len = 0;  
           long long tmpp = n > m ? n : m;  
           m = n > m ? m : n; //m为小的数  
           n = tmpp;//n为大的数计为行  
             
         for(long long i=0;i<(1<<m);i++) //初始化第一行的放置方法数//剔除不合法状态  
         if(judge(i)) //若i状态没有相邻的棋子放在一起  
             {  
            dp[1][num(i)][len]=1;//则第一行状态为len(i)时1的个数为num(i)时的方法数  
            mark[len ++] = i;//标记状态             }  
               
         for(long long i=2;i<=n;i++) //第二行到第n行  
            for(long long j=0;j<=k;j++)//对于放0***n个棋子  
                for(long long x = 0 ; x < len ; x ++) //对于0***len-1个状态(第i行)//枚举  
                {  
                    for(long long y = 0 ; y < len ; y ++)//对于0***len-1个状态(第i-1行)//枚举  
                    {  
                          long long tmp = num (mark[x]);//第i行状态x中1的个数  
                        if(((mark[x] & mark[y]) == 0 ) && j >= tmp) //若上下2行没相邻的且当前的棋子数目大于此行当前状态所用的棋子  
                            dp[i][j][x] += dp[i - 1][j - tmp][y];//放法数可相加  
                            //到当前行共用了j个棋子,当前行用了tmp个棋子,状态为x,到上一行共用了j-tmp个棋子,状态为y  
                    }  
                }  
      for(long long i=0;i< len;i++) //枚举状态相加  
            ans += dp[n][k][i];  
      printf("%lld\n",ans);  
    }  
    return 0;  
} 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园建设方案旨在通过融合先进技术,如物联网、大数据、人工智能等,实现校园的智能化管理与服务。政策的推动和技术的成熟为智慧校园的发展提供了基础。该方案强调了数据的重要性,提出通过数据的整合、开放和共享,构建产学研资用联动的服务体系,以促进校园的精细化治理。 智慧校园的核心建设任务包括数据标准体系和应用标准体系的建设,以及信息化安全与等级保护的实施。方案提出了一站式服务大厅和移动校园的概念,通过整合校内外资源,实现资源共享平台和产教融合就业平台的建设。此外,校园大脑的构建是实现智慧校园的关键,它涉及到数据中心化、数据资产化和数据业务化,以数据驱动业务自动化和智能化。 技术应用方面,方案提出了物联网平台、5G网络、人工智能平台等新技术的融合应用,以打造多场景融合的智慧校园大脑。这包括智慧教室、智慧实验室、智慧图书馆、智慧党建等多领域的智能化应用,旨在提升教学、科研、管理和服务的效率和质量。 在实施层面,智慧校园建设需要统筹规划和分步实施,确保项目的可行性和有效性。方案提出了主题梳理、场景梳理和数据梳理的方法,以及现有技术支持和项目分级的考虑,以指导智慧校园的建设。 最后,智慧校园建设的成功依赖于开放、协同和融合的组织建设。通过战略咨询、分步实施、生态建设和短板补充,可以构建符合学校特色的生态链,实现智慧校园的长远发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值