zoukankan      html  css  js  c++  java
  • 【bzoj3125】CITY 插头dp

    题目描述

    给出一个n*m的矩阵,某些格子不能通过,某些格子只能上下通过或左右通过。求经过所有非不能通过格子的哈密顿回路条数。

    输入

    第一行有两个数N, M表示地图被分割成N*M个块,接下来有N行,每行有M个字符。
     .  表示这个块可以通过
     - 表示这个块只可以左右通过
     | 表示这个块只可以上下通过
     # 表示这个块不能通过
    (从每个块只能走到其上下左右相邻的四个块)

    输出

    一个数,表示小明把所以可以通过的块都经过且只经过一次并回到原地的方案数。

    样例输入

    4 4
    ....
    ..-.
    ....
    ....

    样例输出

    1


    题解

    插头dp

    这道题 的唯一差别在于:部分格子只能上下通过或只能左右通过。

    因此判断条件那里改一改就好了。

    这里学了一下 CQzhangyu 的技♂巧:判断时只需要判断当前状态是否适用于当前格子,以及转移是否适用于当前格子即可。这样不合法的状态就会在下一步剪掉。这一步可以省很大的代码量。

    注意开long long(题面的long指的就是int)

    #include <cstdio>
    #include <cstring>
    typedef long long ll;
    int m , a[13][13] , b[13] , w[1600000] , v[42000] , tot;
    ll f[13][13][42000];
    char str[14];
    void dfs(int p , int c , int now)
    {
    	if(c < 0 || c > m - p + 1) return;
    	if(p > m)
    	{
    		w[now] = ++tot , v[tot] = now;
    		return;
    	}
    	dfs(p + 1 , c , now);
    	dfs(p + 1 , c + 1 , now + b[p]);
    	dfs(p + 1 , c - 1 , now + 2 * b[p]);
    }
    inline int l(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 r(int v , int p)
    {
    	int i , c = 0;
    	for(i = p ; i <= m ; i ++ )
    	{
    		if(v / b[i] % 3 == 1) c ++ ;
    		if(v / b[i] % 3 == 2) c -- ;
    		if(!c) return i;
    	}
    	return -1;
    }
    int main()
    {
    	int n , i , j , k , x , y , p , q;
    	ll 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] == '.' || str[j] == '-') a[i][j] |= 1;
    			if(str[j] == '.' || str[j] == '|') a[i][j] |= 2;
    			if(str[j] != '#') x = i , y = j;
    		}
    	}
    	b[0] = 1;
    	for(i = 1 ; i <= m ; i ++ ) b[i] = b[i - 1] * 3;
    	dfs(0 , 0 , 0);
    	f[0][m][w[0]] = 1;
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		for(j = 1 ; j <= tot ; j ++ )
    			if(v[j] % 3 == 0)
    				f[i][0][j] = f[i - 1][m][w[v[j] / 3]];
    		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((p && !(a[i][j] & 1)) || (q && !(a[i][j] & 2))) continue;
    				if(!a[i][j]) f[i][j][k] += f[i][j - 1][k];
    				else
    				{
    					if(!p && !q && a[i][j] == 3) f[i][j][w[v[k] + b[j - 1] + 2 * b[j]]] += f[i][j - 1][k];
    					if(!p && q && a[i][j] & 1) f[i][j][k] += f[i][j - 1][k];
    					if(p && !q && a[i][j] & 2) f[i][j][k] += f[i][j - 1][k];
    					if(!p && q) f[i][j][w[v[k] + q * (b[j - 1] - b[j])]] += f[i][j - 1][k];
    					if(p && !q) 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[r(v[k] , j)]]] += f[i][j - 1][k];
    					if(p == 2 && q == 2) f[i][j][w[v[k] - 2 * (b[j - 1] + b[j]) + b[l(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 == x && j == y && v[k] == b[j - 1] + 2 * b[j]) ans += f[i][j - 1][k];
    				}
    			}
    		}
    	}
    	printf("%lld
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    C# 访问USB(HID)设备
    IPad C盘中backup文件夹占用太多空间
    fastboot
    adb get android's ip
    Reading MMS
    Bitcoin
    内存问题导致编译不过!
    串口LOG 单编kernel
    Source Insight 格式化
    framework层的空指针会让系统重启
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8134237.html
Copyright © 2011-2022 走看看