轮廓线dp初步

之前断断续续地在轮廓线dp上花了一些时间,尝试秒懂。但是失败了。。今晚花了一下时间,总算初步入门了。

首先根据白书上的解释,轮廓线动归首先要求在一个较窄的棋盘状空间上。

(1)窄,可以理解,状压总不能压爆内存嘛。

(2)棋盘状这个特点,决定了dp从左上到右下的合理性。


忽然想到学这个是因为Poi上有道题被卡住了。。结果翻来翻去翻了翻CDQ论文&PPT

毕竟看ppt讲解然后秒懂都是神犇。理所当然:还是没懂,不明觉厉。

1h过去了。。被自己蠢死了。。


于是去看blog,果然好懂多了。

pistures below are not mine!

----------------------------------------------------------------------------------------------------------------------------------

从入门的题目做起:

hdu1693!入门题oh yeah!

分析题目不难发现要求是所有非障碍格都要有两个插头,左右障碍格没有插头

于是分析开始(举例说明):


 

插头状态{1,0,1,1,1,1} 凹角有两个插头,为了让当前的决策格有且只有两个插头,不加插头。



插头状态{1,0,0,1,1,1} 凹角有一个插头,为了让当前的决策格有且只有两个插头,可有两种选择:右插头或下插头。


插头状态{1,0,0,0,1,1} 凹角有没有插头,为了让当前的决策格有且只有两个插头,必须加右插头和下差头。

至于障碍格,如果没有插头,合法转移,如果有插头,不合法置为0。


有了以上分析,状态也就不难想了:f[i][j][opt],(i,j)为决策格位置,opt为插头的状态。

状态的转移,这道题就按照以上分析的标准转移,但不同的题各不相同。。虽然我自己也还没做。


可以由凸角插头的方案倒推凹角插头方案数,也可以用凹角插头方案数正推凸角插头方案数,看个人思维方式了。

不管正推逆推,答案都是f[n][m][0]。

-------------------------------------------------------------------------------------------------------------------------------------------------------

hdu1693:

Code:

#include<iostream>
#include<cstdio>
#include<cstring> 
#define For(i,l,r) for(int (i)=(l);(i)<=(r);(i)++)
using namespace std;
typedef long long ll;

const int N=13;

ll f[N][N][1<<N],m,n;
int map[N][N];

void dp(){
	memset(f,0,sizeof(f));
	f[0][m][0]=1;
	For(i,1,n){
		For(j,0,(1<<m)-1) f[i][0][j<<1]=f[i-1][m][j];
		For(j,1,m) For(opt,0,(1<<(m+1))-1){
			int x=1<<j,y=1<<(j-1);
			if(map[i][j]){
				if((opt&x)!=0&&(opt&y)!=0) f[i][j][opt]=f[i][j-1][opt-x-y];//凹角有两个插头 凸角不能再有插头 
				else if((opt&x)==0&&(opt&y)==0) f[i][j][opt]=f[i][j-1][opt+x+y]; //凹角没有插头 凸角必须有两个插头
				else f[i][j][opt]=f[i][j-1][opt^x^y]+f[i][j-1][opt]; //凹角一个插头 凸角插头可右可下 
			}else{
				if((opt&x)==0&&(opt&y)==0) f[i][j][opt]=f[i][j-1][opt]; //障碍处没有插头 直接转移 
				else f[i][j][opt]=0; //障碍处出现插头 非法
			} 
		}
	}printf("There are %I64d ways to eat the trees.\n",f[n][m][0]);
	//当前格为(n,m),且轮廓线上不存在任何插头的情况方案数,就是答案 
}

int main(){
	freopen("1693.in","r",stdin);
	int T,cnt=0;
	scanf("%d",&T);
	while(T--){
		cnt++;
		scanf("%d%d",&n,&m);
		For(i,1,n) For(j,1,m)
		scanf("%d",&map[i][j]);
		printf("Case %d: ",cnt);
		dp();
	}
	return 0;
} 


插头状态{1,0,1,1,1,1} 凹角有两个插头,为了让当前的决策格有且只有两个插头,不加插头。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值