ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【插头DP】【学习笔记】

2022-05-04 12:04:05  阅读:172  来源: 互联网

标签:插头 状态 ch cur int pos 笔记 DP


【插头DP】【学习笔记】

Tips:

虽然插头Dp模板是黑的,但其实算法并不难理解,用到的只是轮廓线dp+哈希表而已,比较复杂的是讨论多种情况的转移和位运算,但封装几个函数以后,代码也十分简单了。
模板

Solution

  1. 首先考虑状压dp,考虑需要哪些状态,如果仅仅知道每个格子是否有向下伸出的插头是不够的,例如


    同样是向下伸出4个插头,前者是合法的,后者是非法的(只允许有一个闭合回路)
    因此,必须要知道插头之间的配对情况。一般有最小表示法和括号表示法。
    最小表示法就是从左到右按照顺序给左边的插头标号为1,2,···,n,并给对应的右插头标成同样的数,对于无插头标为0
    括号表示法是有限制条件的,本题中的插头情况一定在任意一个时刻满足,插头两两配对,且不存在交叉,因此可以用括号表示法用一个3进制数表示,其中0表示无插头,1表示左插头,2表示右插头。为了方便利用位运算,通常把它当成4进制计算
  2. 如果按行转移的话,若直接枚举相邻的两行判断能否转移,时间是不够的,但如果枚举一行计算它能转移到哪些行又太复杂。考虑轮廓线dp
  3. 设f[i][j][s]表示枚举到第i行,第j列,轮廓线状态为s,且满足之前的格子全部合法的方案数(合法指每个需要填的格子,一定填,且度为2,不需要填的一定不填,除最后一个能填的格子外,不存在已经封口的闭合回路)
  4. 分类讨论(这个太复杂需要图来说明,就看别人博客吧)
  5. 优化:首先前两维可以滚动,而且发现每一层合法状态很少,所以考虑刷表法,只转移合法的状态,可以用个数据结构将每一层合法的状态存起来,每次遍历这一层所有的合法状态,同时推出下一层所有的合法状态,计算出对应的方案数。因为可能有的状态从好几个状态转移而来,所以用大小与状态数接近的哈希表来维护,复杂度期望是线性的。注意到f数组可以省略掉,直接用hash表进行转移即可。当然这个hash也是滚动的,要开两层。

代码细节

  1. 哈希表对N取模后,值有可能为0,为了方便,hd数组下标从0开始,因此其初值要赋成-1,遍历时用~i来判断。
  2. 只有在最后一个合法格子才能封口统计答案,因此需要提前找到这个点的坐标ex,ey
  3. 每计算完一行,状态是:

    但转移到下一层需要用的是:

    显然这两个侧边始终都是0,原先是0······,现在是······0,相当于所有原先的状态在4进制下左移一格,每计算完一行,以此更新当前状态即可。
  4. 用两个变量表示上层状态lst,这层状态cur,写起代码更直观些。
  5. 封装mk(i,k)函数表示生成一个在第i为k,其余为0的4进制数,方便转移。
  6. 封装get(s,k)函数返回4进制数s的第k位,也是方便转移
  7. 封装update(cur,s,w)表示将第cur层的hash表中状态为s的方案数加w。
  8. 注意在找与某个插头对应的另一插头时,要用sum计数

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
	int x=0,w=1;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-') {w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
inline void write(int x)
{
	if(x<0) putchar('-'),x=~(x-1);
	if(x>9) write(x/10);
	putchar('0'+x%10);
}
const int N=50000;
int n,m,mp[20][20];
int hd[2][N],cnt[2];
struct node{
	int nxt,s,w;
}e[2][N];
int ex,ey;
char str[20];
void update(int cur,int s,int w)
{
	int pos=s%N;
	for(int i=hd[cur][pos];~i;i=e[cur][i].nxt)
	{
		if(e[cur][i].s==s){
			e[cur][i].w+=w;return;
		}
	}
	e[cur][++cnt[cur]].nxt=hd[cur][pos];e[cur][cnt[cur]].s=s;e[cur][cnt[cur]].w=w;hd[cur][pos]=cnt[cur];
}
int get(int s,int pos)
{
	return s>>pos+pos&3;
}
int mk(int pos,int k)
{
	return k*(1<<pos+pos);
}
int res;
void print(int x)
{
	for(int i=0;i<=m;++i) write(x&3),x>>=2;
}
signed main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i){
    	scanf("%s",str+1);
    	for(int j=1;j<=m;++j) if(str[j]=='.') mp[i][j]=1,ex=i,ey=j;
	}
	memset(hd,-1,sizeof hd);
	int cur=0;update(cur,0,1);
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=cnt[cur];++j) e[cur][j].s<<=2;
		for(int j=1;j<=m;++j)
		{
			int lst=cur;cur^=1;memset(hd[cur],-1,sizeof hd[cur]);cnt[cur]=0;
			for(int k=1;k<=cnt[lst];++k)
			{
				int s=e[lst][k].s,w=e[lst][k].w;
				int x=get(s,j-1),y=get(s,j);//cout<<i<<" "<<j<<" "<<x<<" "<<y<<" ";print(s);puts("");
				if(mp[i][j]==0){
					if(!x&&!y) update(cur,s,w);
					continue;
				}
				if(!x&&!y){
					if(mp[i][j+1]&&mp[i+1][j]) update(cur,s+mk(j-1,1)+mk(j,2),w);
					continue;
				}
				if(!x&&y){
					if(mp[i][j+1]) update(cur,s,w);
					if(mp[i+1][j]) update(cur,s-mk(j,y)+mk(j-1,y),w);
					continue;
				}
				if(!y&&x){
					if(mp[i+1][j]) update(cur,s,w);
					if(mp[i][j+1]) update(cur,s-mk(j-1,x)+mk(j,x),w);
					continue;
				}
				if(x==1&&y==1){
					for(int g=j+1,sum=1;g<=m;++g) {
						if(get(s,g)==2){
							sum--;if(sum) continue;
							update(cur,s-mk(j-1,x)-mk(j,y)-mk(g,1),w);break;
						}
						if(get(s,g)) sum++;
					}
					continue;
				}
				if(x==2&&y==2){
					for(int g=j-2,sum=1;g;--g){
						if(get(s,g)==2) sum++;
						else if(get(s,g)){
							sum--;if(sum) continue;
							update(cur,s-mk(j-1,x)-mk(j,y)+mk(g,1),w);break;
						}
					}
					continue;
				}
				if(x-1){
					update(cur,s-mk(j-1,x)-mk(j,y),w);continue;
				}
				if(ex==i&&ey==j){
					res+=w;
				}
			}
		}
	}
	write(res);
	return 0;
}
/*
3 3
...
.*.
...
*/

标签:插头,状态,ch,cur,int,pos,笔记,DP
来源: https://www.cnblogs.com/glq-Blog/p/16220716.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有