状压dp板子 德玛西亚万岁

好吧,虽然它是道板子题,但我还是做了很久,wa了n发,主要还是不能很清晰的整理思路,并且多组样例的清空也总是有遗漏。。。(真是个菜鸡)

链接:登录—专业IT笔试面试备考平台_牛客网
n*m的土地,要求安放士兵,其上下左右不能有其他士兵,并且只有地图上标为1的位置才能放士兵。

 首先不难想到要用状压dp来做。

因为数据范围这么小,答案的量又这么大,并且每一行的每一个位置都会影响下一行的决策。所以我们用01串来压缩状态。(不妨用1来代表士兵,0来代表不妨士兵)

对于每一行的01串(设为st)要考虑这么几个因素:

1.st当中不能有2个相邻的1,代表不存在两个左右相邻的士兵。

2.st与上一行不能在同一个位置同时有1,代表不存在两个上下相邻的士兵。

3.st中有1的位置,地图中对应的地方也一定是1。

用代码来表示的话,分别是:
 

1.if((i&(i<<1))) continue;
(或者:if((i&(i>>1))) continue;
2.if(st&st1) continue;//st1代表上一行的状态
3.if((st&mas[i])!=st) continue;//与地图进行对比 

 然后其实也就差不多了,注意一下多组样例的清空。

我一般是选择用一个vector来存放合法状态,所以要么我就应该在最外面做好预处理,然后内部每次取合法状态的时候注意一下与上界的对比,要么就每次把vector清空,重新记录。

然而我一开始都没做。。。

另外为了优化一下,可以用01串来表示地图,这样也方便我们用位运算来处理。

以上就是所有细节了

然后我们就可以快乐ac了!

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,m,x,ans;
ll mas[14];
vector<ll> vt;
ll dp[14][(1<<13)+10];
#define N 100000000
void init(){
	ans=0;
    memset(mas,0,sizeof mas);
    memset(dp,0,sizeof dp);
	dp[0][0]=1;
    vt.clear();//多组样例这个东西也要清空啊啊啊啊啊啊啊啊!!!!!
}
int main()
{
	while(cin>>n>>m){
		init();
		for(ll i=1;i<=n;++i){
			for(ll j=1;j<=m;++j){
				cin>>x;
				mas[i]*=2;
				mas[i]+=x;//化成01串 
			}
		}
		for(ll i=0;i<(1<<m);++i){
			//if(!(i&(i<<1))&&!(i&(i>>1))) vt.push_back(i);
            if((i&(i<<1))) continue;
         //   if((i&(i>>1))) continue;
                vt.push_back(i);
		}
		for(ll i=1;i<=n;++i){
			for(ll st:vt){//枚举这一行 
				//if((st|mas[i])!=mas[i]) continue;//与地图进行对比 
                  if((st&mas[i])!=st) continue;//与地图进行对比 
				for(ll st1:vt){//枚举上一行 
				//	if(st==st1) continue;
					if(st&st1) continue;
					dp[i][st]+=dp[i-1][st1];
					dp[i][st]%=N;
				}
			}
		}
		for(ll i:vt){
			ans+=dp[n][i];
            ans%=N;
		}
		cout<<ans<<endl;
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值