好吧,虽然它是道板子题,但我还是做了很久,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;
}