zoukankan      html  css  js  c++  java
  • ural 1519 fomular 1 既插头DP学习笔记

    直接看CDQ在2008年的论文吧.
    个人认为她的论文有两个不明确的地方, 这里补充一下: 首先是轮廓的概念. 我们在进行插头DP时, 是从上往下, 从左往右逐个格子进行的, 已经处理的格子与未经处理的格子之间的分界线叫做轮廓线. 因此每个时刻轮廓线的长度都为列数加一. 每次处理下一个格子时, 有且仅有两条轮廓线会变动.
    至于什么是插头, 这个很好理解, 就是从格子里面连出来的线就叫做插头. 不难看出, 在本题中, 一个不可选的格子没有插头; 一个可选的格子有且仅有两个插头. 穿过轮廓线的插头叫做轮廓线上的插头.
    剩下的自己看即可.
    考虑如何DP, 根据论文的描述, 我们发现轮廓线上的插头可以被看作是一个括号序列; 一个合法的状态要求轮廓线也是合法的. 同时为了限制只能存在一条路径, 我们需要在代码中加以体现.

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    const int N = 15, M = 15, SZ = (int)3e5, STT = 1594323;
    int n, m;
    char s[N][M];
    int pw[M], enc[STT], dec[SZ], tp; // 分别表示加密和解密
    long long f[SZ], g[SZ];
    inline int check(int stt)
    {
    	int cnt = 0;
    	for(int i = 0; i < m + 1; ++ i)
    	{
    		if(stt / pw[i] % 3 == 1) ++ cnt; else if(stt / pw[i] % 3 == 2) -- cnt;
    		if(cnt < 0) return 0;
    	}
    	return ! cnt;
    }
    inline void initialize()
    {
    	pw[0] = 1; for(int i = 1; i <= m + 1; ++ i) pw[i] = pw[i - 1] * 3;
    	tp = 0;
    	for(int i = 0; i < pw[m + 1]; ++ i) if(check(i)) enc[i] = tp, dec[tp ++] = i;
    }
    inline int get(int stt, int pos) {return stt / pw[pos] % 3;}
    int main()
    {
    
    #ifndef ONLINE_JUDGE
    
    	freopen("ural1519.in", "r", stdin);
    	freopen("ural1519.out", "w", stdout);
    
    #endif
    
    	int lstX = -1, lstY = -1;
    	scanf("%d %d
    ", &n, &m); for(int i = 0; i < n; ++ i) scanf("%s", s[i]);
    	for(int i = 0; i < n; ++ i) for(int j = 0; j < m; ++ j) if(s[i][j] == '.') lstX = i, lstY = j;
    	initialize();
    	memset(g, 0, sizeof(g)); g[enc[0]] = 1;
    	for(int i = 0; i < n; ++ i)
    	{
    		for(int j = 0; j < m; ++ j)
    		{
    			swap(f, g); memset(g, 0, sizeof(g));
    			if(s[i][j] == '*')
    			{
    				for(int k = 0; k < tp; ++ k) if(! get(dec[k], j) && ! get(dec[k], j + 1)) g[k] = f[k];
    				continue;
    			}
    			else for(int k = 0; k < tp; ++ k) if(f[k])
    			{
    				int x = get(dec[k], j), y = get(dec[k], j + 1);
    				if(! x && ! y) g[enc[dec[k] + pw[j] + pw[j + 1] * 2]] += f[k];
    				else if(! x ^ ! y)
    				{
    					g[k] += f[k];
    					g[enc[dec[k] + (y - x) * pw[j] + (x - y) * pw[j + 1]]] += f[k];
    				}
    				else if(x == 1 && y == 1)
    				{
    					int p = j + 1, cnt = 0;
    					for(; p <= m; ++ p)
    					{
    						if(get(dec[k], p) == 1) ++ cnt; else if(get(dec[k], p) == 2) -- cnt;
    						if(! cnt) break;
    					}
    					g[enc[dec[k] - pw[j] - pw[j + 1] - pw[p]]] += f[k];
    				}
    				else if(x == 2 && y == 2)
    				{
    					int p = j, cnt = 0;
    					for(; ~ p; -- p)
    					{
    						if(get(dec[k], p) == 2) ++ cnt; else if(get(dec[k], p) == 1) -- cnt;
    						if(! cnt) break;
    					}
    					g[enc[dec[k] - 2 * pw[j] - 2 * pw[j + 1] + pw[p]]] += f[k];
    				}
    				else if(x == 1 && y == 2 && i == lstX && j == lstY) g[enc[dec[k] - pw[j] - 2 * pw[j + 1]]] += f[k];
    				// 这里就是限制只能存在一条路径的关键
    				else if(x == 2 && y == 1) g[enc[dec[k] - 2 * pw[j] - pw[j + 1]]] += f[k];
    			}
     		}
     		swap(f, g); memset(g, 0, sizeof(g));
     		for(int j = 0; j < tp && dec[j] < pw[m]; ++ j) g[enc[dec[j] * 3]] += f[j];
    	}
    	printf("%lld
    ", f[enc[0]]);
    }
    
    
  • 相关阅读:
    .Net并行编程
    ShopEx4.8.5.55328破解版
    PLinq
    C# 4.0 Parallel
    WCF、Web API、WCF REST、Web Service
    WCF 采用net.tcp协议
    MVC 过滤器3
    go orcale
    获取合并单元格中值的一个方法POI
    发起、维持和发展以利润为导向的企业的有目的性的行为(转)
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/7490321.html
Copyright © 2011-2022 走看看