zoukankan      html  css  js  c++  java
  • 【luogu P1879】Corn Fields G / 玉米田++ / 玉米田(加加强版)(状压DP)(轮廓线DP)

    Corn Fields G / 玉米田++ / 玉米田(加加强版)

    题目链接:luogu P1879

    题目大意

    给你一个 n*m 的矩阵,有一些位置可以选放不放东西。
    然后规定一个东西旁边四个位置不能有东西。
    问你有多少种放的方案。

    思路

    首先不会加强版的自己先回去看看我的加强版,本文是在那个的基础上继续搞,所以不会讲思路,而是讲如何优化。
    ——>加强版做法<——

    然后我们发现第一个问题是空间,会爆。
    你直接一个 (f) 数组就爆了。
    但你仔细想想会发现有一些状态根本就是不合法的。
    那你轮廓线只会有一个断点,那一个顶多就会有一个地方连起来。
    那如果有多个地方连起来,就说明这个状态就是不合法的。
    你可以把合法的找出来重新编号,经我的测试(不一定准),最多不超过 (13) 万。
    你就搞定了空间。

    然后你枚举也只用枚举这些位置,然后你会发现你时间也可以了。
    。。。
    然后虽然大数据在本地跑要 2s,甚至会 5s,但它会自动给你开 O2,在 OJ 上还是能过的。
    然后就好啦。

    代码

    #include<cstdio>
    #include<cstring>
    #define mo 100000000
    
    using namespace std;
    
    bool a[121][21];
    int n, m, now, re;
    int f[2][130001], ans, tot, num;
    int to[2097152], fr[130001], yes;
    char c;
    
    int read() {
    	re = 0;
    	c = getchar();
    	while (c < '0' || c > '9') c = getchar();
    	while (c >= '0' && c <= '9') {
    		re = (re << 3) + (re << 1) + c - '0'; 
    		c = getchar();
    	}
    	return re;
    }
    
    int main() {
    //	freopen("cowfood.in", "r", stdin);
    //	freopen("cowfood.out", "w", stdout);
    	
    	n = read(); m = read();
    	for (int i = 1; i <= n; i++)
    		for (int j = 0; j < m; j++)
    			a[i][j] = read();
    	
    	memset(to, -1, sizeof(to));//把有用的状态找出来
    	for (int i = 0; i < (1 << m); i++) {
    		num = 0; yes = 2;
    		for (int j = 0; j < m; j++)
    			if ((i >> j) & 1) {
    				num++;
    				if (num == 2 && yes == 2) {
    					yes = 1;
    					num = 1;
    				}
    				else if (num == 2 && yes == 1) {
    					yes = 0;
    					break;
    				}
    			}
    			else num = 0;
    		if (yes) {
    			to[i] = tot++;
    			fr[tot - 1] = i;
    		}
    	}
    	
    	f[0][0] = 1; now = 0; 
    	for (int i = 1; i <= n; i++)
    		for (int j = 0; j < m; j++) {
    			now ^= 1;
    			for (int k = 0; k < tot; k++) f[now][k] = 0;
    			for (int k = 0; k < tot; k++) {
    				int up = (1 << j) & fr[k], lft = (j == 0 ? 0 : (1 << (j - 1)) & fr[k]);
    				if (i == 1 && up) continue;
    				if (j == 0 && lft) continue;
    				if (up) {
    					if (to[fr[k] ^ (1 << j)] == -1) continue;
    					f[now][to[fr[k] ^ (1 << j)]] += f[now ^ 1][k];
    					if (f[now][to[fr[k] ^ (1 << j)]] > mo) f[now][to[fr[k] ^ (1 << j)]] -= mo;
    					continue;
    				}
    				if (lft || !a[i][j]) {
    					f[now][k] += f[now ^ 1][k];
    					if (f[now][k] > mo) f[now][k] -= mo;
    					continue;
    				}
    				f[now][k] += f[now ^ 1][k];
    				if (f[now][k] > mo) f[now][k] -= mo;
    				if (to[fr[k] ^ (1 << j)] == -1) continue;
    				f[now][to[fr[k] ^ (1 << j)]] += f[now ^ 1][k];
    				if (f[now][to[fr[k] ^ (1 << j)]] > mo) f[now][to[fr[k] ^ (1 << j)]] -= mo;
    			}
    		}
    	
    	for (int i = 0; i < tot; i++)
    		ans = (ans + f[now][i]) % mo;
    	printf("%d", ans);
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    
  • 相关阅读:
    一定间隔时间下重复执行一个函数的几个方法
    关于 MonoDevelop on Linux 单步调试问题的解决
    MonoDevelop 4.2.2/Mono 3.4.0 in CentOS 6.5 安装笔记
    在ASP.NET MVC 4 on Mono中使用OracleClient in CentOS 6.x的问题记录
    在CentOS 6.4 x86_32中使用Rhythmbox听MP3
    MonoDevelop 4.0.9 on CentOS 6.3 安装笔记
    MemoryMappedFile 在 Mono in Linux 的开发笔记
    Mono on CentOS 6.3 安装笔记
    庆祝下:iOS 开发者企业级计划(299美元/年帐户+邓白氏码免费) 和 Windows Phone公司应用(公司帐户99美元+Symantec企业证书299美元/年))顺利发布成功
    PowerDesigner中NAME和COMMENT的互相转换,需要执行语句
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/jzoj_7200.html
Copyright © 2011-2022 走看看