• 【ybtoj】【状压dp】种植方案


    题意

    题目描述

    农场主新买了一块长方形的新牧场,这块牧场被划分成m行n列,每一格都是一块正方形的土地。农场主打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

    遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是农场主不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。 如果不考虑草地的总块数,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

    输入格式

    第1行:两个整数 m和 n,用空格隔开。
    第2到第m+1行:每行包含 n个用空格隔开的整数,描述了每块土地的状态。第i+1行描述了第i行的土地,所有整数均为 0或 1,1表示这块土地足够肥沃, 0则表示这块土地不适合种草。

    输出格式

    一个整数,即牧场分配总方案数除以1e8的余数。

    样例

    输入样例

    2 3  
    1 1 1  
    0 1 0
    

    输出样例

    9
    

    数据范围与提示

    对于100%的数据,满足n,m ∈[1,12]。

    分析

    十分经典的状压dp,很好想但是涉及一些状压dp的细节和简单优化,类似于模板,需要研究透彻!

    优化:

    1. 用jud数组先预处理出第 i 行 j 的状态可不可行,循环内就可以O(1)判断
    2. 由于本身每一行有一些位置不能放,即有一些状态不可行,可以给每个合法的状态一个编号,让编号作为第二维,可以减少一些空间和时间(本代码中没有涉及)

    注意事项

    代码中关键部分和第一次的错误都已经用注释标注出来!ovo

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int INF = 0x3f3f3f3f,mod = 1e8;
    int dp[13][1<<13];//dp[i][j]表示第i行状态为j时的方案数 
    int n,m;
    bool jud[13][1<<13];
    int a[13][13];
    int main()
    {
    	scanf("%d%d",&m,&n);
    	for(int i=1;i<=m;i++)
    		for(int j=1;j<=n;j++)
    			scanf("%d",&a[i][j]);
    	//预处理 
    	for(int i=1;i<=m;i++)//第i行 
    		for(int j=0;j<(1<<n);j++)//状态为j 
    			{
    				if(j&(j>>1))//如果有相邻土地被用 
    				{	
    					jud[i][j]=1;
    					continue;
    				}
    				for(int k=1;k<=n;k++)	
    					if( (j&(1<<(k-1)))&& !a[i][n-k+1])//如果有不能种的地方种了东西 
    					{
    						jud[i][j]=1;
    						break;
    					}
    			}
    	/*
    	for(int i=0;i<(1<<n);i++)
    		dp[1][i]=1;//初始化第1行任何状态都为1 
    	//注意:原来初始化第0行状态都为1,同时i从1开始枚举,这样是不对的 
    	*/
      	//
    	dp[0][0]=1;//初始化第0行为0状态,可以转移到第1行任何状态且不重复,和上面的初始化等效 
    	for(int i=1;i<=m;i++)//根据初始化注意i从几开始枚举 
    		for(int j=0;j<(1<<n);j++)//枚举当前行状态 
    		{
    			if(jud[i][j]) continue;
    			for(int k=0;k<(1<<n);k++)//枚举上一行状态 
    			{
    				if(jud[i-1][k]||(k&j)) continue;
    				dp[i][j]=((ll)dp[i][j]+dp[i-1][k])%mod;
    			}
    		}
    	ll ans=0;
    	for(int i=0;i<(1<<n);i++)
    	{
    		if(jud[m][i]) continue;//注意这里的判断,如果m==1时不加会错 
    		ans=(ans+dp[m][i])%mod;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    剑指 Offer 50. 第一个只出现一次的字符
    剑指 Offer 42. 连续子数组的最大和
    剑指 Offer 41. 数据流中的中位数
    剑指 Offer 40. 最小的k个数
    剑指 Offer 39. 数组中出现次数超过一半的数字
    剑指 Offer 38. 字符串的排列
    MySQL更改密码
    WPF中的MySQLHelper
    WPF多线程
    mysql-5.7.28-winx64(压缩包)安装教程
  • 原文地址:https://www.cnblogs.com/conprour/p/15045705.html
Copyright © 2020-2023  润新知