首页 > 其他分享 >插头 dp 小记

插头 dp 小记

时间:2024-03-13 21:55:25浏览次数:25  
标签:插头 ll 括号 maxn hd 小记 dp define

这里默认各位都会轮廓线 dp。

引入

题意:给出 \(n\times m\) 的方格,有些格子不能铺线,其它格子必须铺,可以形成多个闭合回路。问有多少种铺法?

\(2\le n,m\le 12\)

考虑如果每个格子和相邻格子(包括边缘)的衔接都合法,最终一定能形成若干个闭合回路,因此我们只需要在这个条件上 dp。

设 \(f[i,j,S]\) 表示到达 \((i,j)\) 时上方的轮廓线状态是 \(S\) 的方案数。

这里,我们称轮廓线中向下的延伸和当前格子左侧向右的延伸叫作 插头

每次填的格子有六种可能:

然后分类讨论上面和左边分别有没有插头。

  • 都没有:当前格子必须往右边和下面各延伸一个插头

  • 上面有:可以把上面的插头向下延伸,或者拐弯向右延伸

  • 左边有:可以把左边的插头向右延伸,或者拐弯向下延伸

  • 都有:把这两个插头合并

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define mkp make_pair
#define fi first
#define se second
#define pir pair<ll,ll>
#define pb push_back
using namespace std;
const ll maxn=13;
ll t,n,m,f[maxn*maxn][1<<12][2],lim;
void ad(ll &x,const ll &y) {x+=y;}
int main(){
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&n,&m); lim=(1<<m)-1;
		memset(f,0,sizeof f);
		f[0][0][0]=1;
		for(ll i=1;i<=n;i++){
			for(ll j=1;j<=m;j++){
				ll x=(i-1)*m+j, w; scanf("%lld",&w);
				for(ll S=0;S<=lim;S++){
					if(w){
						if(S&(1<<m-1)){
							ad(f[x][(S<<1)&lim|1][0],f[x-1][S][0]);
							ad(f[x][(S<<1)&lim][0],f[x-1][S][1]);
							if(j<m) ad(f[x][(S<<1)&lim][1],f[x-1][S][0]);
						} else{
							if(j<m) ad(f[x][S<<1][1],f[x-1][S][1]);
							ad(f[x][S<<1|1][0],f[x-1][S][1]);
							if(j<m) ad(f[x][S<<1|1][1],f[x-1][S][0]);
						}
					} else{
						if(!(S&(1<<m-1))){
							ad(f[x][S<<1][0],f[x-1][S][0]);
						}
					}
				}
			}
		}
		printf("%lld\n",f[n*m][0][0]);
	}
	return 0;
}

问题

题意:给出 \(n\times m\) 的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路。问有多少种铺法?

\(2\le n,m\le 12\)

这和上题唯一的区别在于必须恰好形成一个闭合回路。

每次维护的 \(S\) 包括左边的插头,是一个插头集合,我们可以多加一点信息来表示所有插头的连通性情况。

具体的,用最小表示法来表示一个插头连通性状态,然后哈希映射到一个较小的整数状态上。

但是还有更好的方法。如果算上左边的插头,我们把插头集合按 \(1\sim m\)(左边的插头所处位置是一个缝隙)的位置顺序来观察,会发现他们的连通性恰好形成一个括号序列。

具体的,比如:

\[\text{()(()())} \]

\[\text{1 2 3 4 5 6 7 8} \]

那么第 \(1\) 个和 第 \(2\) 个插头连通,第 \(3\) 个和 第 \(8\) 个插头连通,第 \(6\) 个和第 \(7\) 个插头连通。

所以我们可以用一个三进制数来表示状态,分别表示 无插头/左括号/右括号。

设 \(p_1,p_2\) 分别表示左边和上方位置的单个位置的状态,分类讨论:

  • \(p_1=p_2=0\):我们需要新开两个插头,一个向右,一个向下。

  • \(p_1=0,p_2>0\):此时只有上方有插头,可以继续向下延伸,也可以拐弯向右。

  • \(p_1>0,p_2=0\):此时只有左边有插头,可以继续向右延伸,也可以拐弯向下。

  • \(p_1=p_2=1\):两个都是“左括号”插头。合并后长这样:

会发现蓝色和绿色的右括号连通,可以视绿色的右括号为左括号。

具体的,我们向后找到匹配上插头的右括号,变为左括号。

  • \(p_1=1,p_2=2\):由于两个插头早已连通,此时合并完后会形成一个完整的回路,判断当前格子是否为 \((n,m)\),只有在 \((n,m)\) 时才会合并出这样一个回路

  • \(p_1=2,p_2=1\):直接合并。

  • \(p_1=p_2=2\):类似于 \(p_1=p_2=1\) 的情况,我们向前面找到匹配左插头的左括号,变成右括号。

然后状态数是严重不满的,我们考虑使用哈希表存下所有有用状态,或者 unordered_map。

那么现在的时间就和有用状态有关了。为了方便,可以用四进制数代替三进制数。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define mkp make_pair
#define fi first
#define se second
#define pir pair<ll,ll>
#define pb push_back
using namespace std;
const ll maxn=20, M=3e5+10;
ll n,m,z,tot[2],st[2][M],f[2][M],bit[maxn],ex,ey,ans,b[maxn][maxn];
char a[maxn][maxn];
struct hash_table{
	ll hd[M],nxt[M];
	void ins(ll x,ll v){
		ll p=x%299993+1;
		for(ll i=hd[p];i;i=nxt[i])
			if(st[z][i]==x){
				f[z][i]+=v; return;
			}
		nxt[++tot[z]]=hd[p], hd[p]=tot[z];
		st[z][hd[p]]=x, f[z][hd[p]]=v;
	}
	void clr(){
		memset(hd,0,sizeof hd);
		tot[z]=0;
	}
}H;
int main(){
	scanf("%lld%lld",&n,&m);
	for(ll i=1;i<=n;i++){
		scanf("%s",a[i]+1);
		for(ll j=1;j<=m;j++){
			if(a[i][j]=='.') ex=i, ey=j;
			b[i][j]=(a[i][j]=='.');
		}
	}
	bit[1]=1;
	for(ll i=2;i<=m+1;i++) bit[i]=bit[i-1]<<2;
	H.ins(0,1);
	for(ll i=1;i<=n;i++){
		for(ll j=1;j<=tot[z];j++) st[z][j]<<=2;
		for(ll j=1;j<=m;j++){
			z^=1, H.clr();
			for(ll k=1;k<=tot[z^1];k++){
				ll msk=st[z^1][k], val=f[z^1][k], p1=(msk>>(j-1<<1))&3, p2=(msk>>(j<<1))&3;
				if(a[i][j]=='*'){
					if(!p1&&!p2) H.ins(msk,val);
				} else{
					if(!p1&&!p2&&b[i][j+1]&&b[i+1][j]) H.ins(msk+bit[j]+2*bit[j+1],val);
					if(!p1&&p2){
						if(b[i][j+1]) H.ins(msk,val);
						if(b[i+1][j]) H.ins(msk-p2*bit[j+1]+p2*bit[j],val);
					}
					if(p1&&!p2){
						if(b[i+1][j]) H.ins(msk,val);
						if(b[i][j+1]) H.ins(msk-p1*bit[j]+p1*bit[j+1],val);
					}
					if(p1==1&&p2==1){
						ll c=1;
						for(ll r=j+2;r<=m+1;r++){
							ll x=(msk>>(r-1<<1))&3;
							if(x==1) ++c;
							if(x==2) --c;
							if(!c){
								H.ins(msk-bit[j]-bit[j+1]-bit[r],val);
								break;
							}
						}
					}
					if(p1==1&&p2==2){
						if(i==ex&&j==ey) ans+=val;
					}
					if(p1==2&&p2==1) H.ins(msk-2*bit[j]-bit[j+1],val);
					if(p1==2&&p2==2){
						ll c=1;
						for(ll r=j-1;r;r--){
							ll x=(msk>>(r-1<<1))&3;
							if(x==2) ++c;
							if(x==1) --c;
							if(!c){
								H.ins(msk-2*(bit[j]+bit[j+1])+bit[r],val);
								break;
							}
						}
					}
				}
			}
		}
	}
	printf("%lld",ans);
	return 0;
}

标签:插头,ll,括号,maxn,hd,小记,dp,define
From: https://www.cnblogs.com/Sktn0089/p/18071631

相关文章

  • TCP和UDP
    计算机网络的七层协议计算机网络体系可以大致分为一下三种,OSI七层模型、TCP/IP四层模型和五层模型。七层协议的功能:物理层:负责处理网络通信的物理传输和传输介质的细节。物理层的主要任务是将比特流(bitstream)从发送方传输到接收方,确保数据能够在网络中进行可靠的物理传输。......
  • 序列 DP
    (1)LOJP507接竹竿有\(n\)张牌排成一排,每张牌有属性\((c_i,v_i)\)。保证\(c_i\lek\)。每次操作选择两张牌\(l,r\)满足\(c_l=c_r\),删除\(l\simr\)中的所有牌,并获得\(\sum_{i=l}^rv_i\)的收益。求最大的收益。\(n,k\le10^6\)。设状态\(f_i\)表......
  • Offer必备算法13_路径dp_六道力扣题详解(由易到难)
    目录①力扣62.不同路径解析代码②力扣63.不同路径II解析代码③力扣LCR166.珠宝的最高价值解析代码④力扣931.下降路径最小和解析代码⑤力扣64.最小路径和解析代码⑥力扣174.地下城游戏解析代码本篇完。①力扣62.不同路径62.不同路径难度中等一个......
  • 从CF1941D与1741E初探可达性DP
    Problem-D-Codeforces用记忆化搜索过的,然而DP能快300ms记忆化搜索|\(\texttt{set}\)模拟核心思路一致,都是通过定义一个状态,即在第t次到达第now点来去重剪枝记忆化搜索intn,m,x;std::vector<std::pair<int,char>>step;std::set<int>S;intgetClock(intx,......
  • 【Paper Reading】7.DiT(VAE+ViT+DDPM) Sora的base论文
    VAEDDPM 分类内容论文题目ScalableDiffusionModelswithTransformers作者WilliamPeebles(UCBerkeley),SainingXie(NewYorkUniversity)发表年份2023摘要介绍了一类新的扩散模型,这些模型利用Transformer架构,专注于图像生成的潜在扩散模型。这些......
  • GDPU JavaWeb JSP基础
    正式走进Javaweb大门,了解jsp及Java在前端的体现。JSP JSP,JavaServerPages是一种基于Java技术的服务器端动态网页技术,允许开发人员在HTML页面中嵌入Java代码。通过JSP,开发人员可以创建包含静态模板和动态内容的网页。当客户端请求一个包含JSP的网页时,服务器会执行其中的J......
  • GDPU unity游戏开发 滚动小球
      解锁你的游戏大门,适合小白入门看的,通过简单的实例大概了解unity的一些基本操作。常用快捷键 CtrlC/V/X/Z对应复制/粘贴/剪切/回退很多小白都惟手熟尔了,W物体对象的位置/平移/移动 ,E物体对象旋转,R物体对象缩放,Q/Alt中键用于场景的移动,右键/Alt左键用于场景的旋转,滚......
  • 高dpi下,Vb.net调整控件位置的小经验
     高dpi下,Vb.net调整控件位置的小经验 boy8199/3vdo/club最近写了一个捕快TXT网文采集软件,结果发现在DPI不同的情况下,软件布局会变形.找了半天原因才发现是DPI的问题,默认系统的dpi是96(100%)现在显示器的屏幕比较大,所以好多人会把显示放大到125%或150%导致程序控件变形......
  • 【蓝桥杯】(台阶方案dp)
    #include<iostream>usingnamespacestd;#defineLLlonglongconstintN=1e6+5;constLLp=1e9+7;LLdp[N];inta[3];//intmain(){LLn;cin>>n;cin>>a[0]>>a[1]>>a[2];dp[0]=1;//当目标值为0时,只有一种方案for(......
  • 区间 DP
    P3146[USACO16OPEN]248G给定一个序列,每次可以将两个相邻且相同的数合并成一个数,合并结果为原数加一。求最后能得到的最大数字。\(n\le248\),\(1\lea_i\le40\)。最暴力的,设状态\(f_{l,r,k}\)表示区间\([l,r]\)能否最终合并为数字\(k\)。也就是说\(f_{l......