zoukankan      html  css  js  c++  java
  • 【bzoj1814】Ural 1519 Formula 1 插头dp

    题目描述

    一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数。

    输入

    The first line contains the integer numbers N and M (2 ≤ N, M ≤ 12). Each of the next N lines contains M characters, which are the corresponding cells of the rectangle. Character "." (full stop) means a cell, where a segment of the race circuit should be built, and character "*" (asterisk) - a cell, where a gopher hole is located.

    输出

    You should output the desired number of ways. It is guaranteed, that it does not exceed 2^63-1.

    样例输入

    4 4
    **..
    .
    ...
    ....
    ....

    样例输出

    2


    题解

    插头dp板子题

    具体讲解可以参考  陈丹琦《基于连通性状态压缩的动态规划问题》  以及  Dalao博客  。

    我的代码中使用了三进制状态,先使用dfs预处理出所有合法的广义括号序列,用0表示没有插头,1表示左括号插头,2表示右括号插头。处理好状态与编号的对应关系后进行dp,复杂度就只与合法状态数有关了。

    时间复杂度 $O(合法状态数·nm^2)$ ,经计算合法状态数不超过42000。而实际上这个时间复杂度远远达不到上限,因为只有两个插头是双左括号或双右括号时才会产生最后的m,且这个m也是不满的。因此可以过。

    注意需要long long。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int m , a[14][14] , v[42000] , w[1600000] , tot , b[14];
    long long f[13][13][42000];
    char str[14];
    void dfs(int p , int c , int now)
    {
    	if(c < 0 || c > m + 1 - p + 1) return;
    	if(p > m + 1)
    	{
    		v[++tot] = now , w[now] = tot;
    		return;
    	}
    	dfs(p + 1 , c , now);
    	dfs(p + 1 , c + 1 , now + b[p - 1]);
    	dfs(p + 1 , c - 1 , now + 2 * b[p - 1]);
    }
    inline int left(int v , int p)
    {
    	int i , c = 0;
    	for(i = p ; ~i ; i -- )
    	{
    		if(v / b[i] % 3 == 1) c -- ;
    		if(v / b[i] % 3 == 2) c ++ ;
    		if(!c) return i;
    	}
    	return -1;
    }
    inline int right(int v , int p)
    {
    	int i , c = 0;
    	for(i = p ; i <= m ; i ++ )
    	{
    		if(v / b[i] % 3 == 2) c -- ;
    		if(v / b[i] % 3 == 1) c ++ ;
    		if(!c) return i;
    	}
    	return -1;
    }
    int main()
    {
    	int n , i , j , k , nn , mm , p , q;
    	long long ans = 0;
    	scanf("%d%d" , &n , &m);
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		scanf("%s" , str + 1);
    		for(j = 1 ; j <= m ; j ++ )
    			if(str[j] == '.')
    				a[i][j] = 1 , nn = i , mm = j;
    	}
    	b[0] = 1;
    	for(i = 1 ; i <= m ; i ++ ) b[i] = b[i - 1] * 3;
    	dfs(1 , 0 , 0);
    	f[1][0][1] = 1;
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		for(j = 1 ; j <= m ; j ++ )
    		{
    			for(k = 1 ; k <= tot ; k ++ )
    			{
    				p = v[k] / b[j - 1] % 3 , q = v[k] / b[j] % 3;
    				if(!a[i][j])
    				{
    					if(!p && !q) f[i][j][k] += f[i][j - 1][k];
    				}
    				else
    				{
    					if(!p && !q && a[i][j + 1] && a[i + 1][j]) f[i][j][w[v[k] + b[j - 1] + 2 * b[j]]] += f[i][j - 1][k];
    					if(!p && q)
    					{
    						if(a[i][j + 1]) f[i][j][k] += f[i][j - 1][k];
    						if(a[i + 1][j]) f[i][j][w[v[k] + q * (b[j - 1] - b[j])]] += f[i][j - 1][k];
    					}
    					if(p && !q)
    					{
    						if(a[i + 1][j]) f[i][j][k] += f[i][j - 1][k];
    						if(a[i][j + 1]) f[i][j][w[v[k] + p * (b[j] - b[j - 1])]] += f[i][j - 1][k];
    					}
    					if(p == 1 && q == 1) f[i][j][w[v[k] - b[j - 1] - b[j] - b[right(v[k] , j)]]] += f[i][j - 1][k];
    					if(p == 2 && q == 2) f[i][j][w[v[k] - 2 * b[j - 1] - 2 * b[j] + b[left(v[k] , j - 1)]]] += f[i][j - 1][k];
    					if(p == 2 && q == 1) f[i][j][w[v[k] - 2 * b[j - 1] - b[j]]] += f[i][j - 1][k];
    					if(p == 1 && q == 2 && i == nn && j == mm) ans += f[i][j - 1][k];
    				}
    			}
    		}
    		if(i != n)
    			for(k = 1 ; k <= tot ; k ++ )
    				if(v[k] % 3 == 0)
    					f[i + 1][0][k] += f[i][m][w[v[k] / 3]];
    	}
    	printf("%lld
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    为什么hive表有数据,但count(*)返回0
    数仓建设时,要建历史表,用于保存历史数据,用于日后出问题时,起修复数据的作用。按日期分区,每天都把所有的数据存到当天的分区里
    get_json_object用以获取json类型的字段的值
    str_to_map语句,字符串类型变map类型
    按更新时间取最新记录
    hive临时表
    数仓分层
    次日留存、七日留存
    转义
    数据库三范式
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8127185.html
Copyright © 2011-2022 走看看