• AcWing 327. 玉米田(状态压缩动态规划)


    题目描述

    农夫约翰的土地由(M imes N) 个小方格组成,现在他要在土地里种植玉米。

    非常遗憾,部分土地是不育的,无法种植。

    而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘

    (注意:这里是上下左右边缘,不是两斜对角边缘)

    现在给定土地的大小,请你求出共有多少种种植方法

    土地上什么都不种也算一种方法。

    输入格式

    (1)行包含两个整数(M)(N)

    (2 dots M+1)行:每行包含(N)个整数(0)(1),用来描述整个土地的状况,(1)表示该块土地肥沃,(0)表示该块土地不育。

    输出格式

    输出总种植方法对(100000000)取模后的值。

    数据范围

    [1 le M,N le 2 ]

    输入样例:

    2 3
    1 1 1
    0 1 0
    

    输出样例:

    9
    

    算法解析

    配合算法进阶课y总讲解视频使用更佳。

    算法构造

    经典的棋盘型状态压缩动态规划,我们可以按照之前Acwing上P1064小国王的思路,处理本题。

    首先,我们需要明确,题目的要求:

    1. 统计方案数
    2. 有些土地不能种植

    状态设计

    首先,我们得明确状态是什么。

    我们这个状态,肯定是要统计方案数。

    我们这个状态,必然需要表示每一行土地种植的状态。

    因此得到:

    [f[i][s]表示已经种植前i行,且第i行种植的状态为s的方案数 ]

    状态转移

    题目的限制条件,其实就是我们转移的限制条件。

    我们知道,这里是十字形的禁止种植,也就是上下左右不能有相邻的两棵玉米。

    那么怎么判断呢?

    如果说我们把(1)表示这个地方种植玉米,(0)表示不种植

    [S=1110 quad 1,2,3这三个地方种玉米,第四个地方不种植玉米 ]

    对于一行而言,不能种植相邻的玉米。

    即:

    对于一行而言,不能有相邻的(1)

    [S=1110 quad 是不合法的状态 ]

    对于相邻的两行而言,不能在同一列都种植玉米

    [a=1010 \\ b=1000 \\ 这是不可以的,在第一个位置会出现上下矛盾 ]

    那么我们可以转化为:

    [a & b==0 ]

    最后,对于题目中的土地不能种植,我们可以认为。

    [如果第i行的状态为s,那么荒废土地处不能有1 ]

    我们可以设计一个数组:

    [g[i]表示第i行不能种植土地的状态 \\ g[1]=1011 quad 表示第一行,第一个,第三个,第四个位置不能种植玉米 ]

    总而言之

    [第i行的状态为s \\ 那么s & g[i]==0 ]

    代码解析

    #include <bits/stdc++.h>
    using namespace std;
    const int N=13,Mod=100000000;
    vector<int> state,head[1<<N];
    int n,m,x,f[14][1<<N],g[N];
    inline bool check(int x)//快速判断有没有相邻的1
    {
    	return !(x&x>>1);
    }
    inline void init()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1; i<=n; i++)
    		for (int j=1; j<=m; j++)
    		{
    			scanf("%d",&x);
    			g[i]+=(!x<<(j-1));//荒废土地是0,我们在这里转换为1
    		}
    	for(int i=0; i<(1<<m); i++)
    		if (check(i))//这个状态不存在种植左右相邻的玉米
    			state.push_back(i);
    	for(int i=0; i<state.size(); i++)
    		for(int j=0; j<state.size(); j++)
    			if (!(state[i] & state[j]))//i对应的状态和j对应的状态没有在同一列种植玉米
    				head[i].push_back(j);
    	f[0][0]=1;
    	for(int i=1; i<=n+1; i++)
    		for(int a=0; a<state.size(); a++)
    		{
    			if (state[a] & g[i])//在第i行,状态a是否满足在荒废土地没有种植玉米
    				continue;
    			for(int b=0; b<head[a].size(); b++)//从上一行b对应的状态,转到本行a对应的状态
    				f[i][a]=(f[i][a]+f[i-1][head[a][b]])%Mod;
    		}
    	printf("%d
    ",f[n+1][0]);//表示第n+1行什么都没种植的状态,其实就是累加f[n][S]
    }
    signed main()
    {
    	init();
    	return 0;
    }
    
  • 相关阅读:
    iBatis查询时报“列名无效”无列名的错误原因及解决方法
    【转】Spring结合Quartz实现多任务定时调用
    关于jar中读取包内和包外文件
    【摘自百度文库】数据库水平切分的实现原理解析
    web.xml 中的listener、 filter、servlet 加载顺序及其详解(转载)
    EL表达式
    【转】使用XFire+Spring构建Web Service
    慎用href="javascript:void(0)"
    POI导出EXCEL【摘自:oschina.net】
    【转】IBM websphere6.1 不支持泛型、intInteger类型的自动装箱和拆箱问题
  • 原文地址:https://www.cnblogs.com/gzh-red/p/13419691.html
Copyright © 2020-2023  润新知