题目:click POJ-1185
题意:在一个给定的字符串地图里放炮兵,炮兵的攻击范围,上下两格,左右两格,H的地方不能放炮兵,炮兵相互之间攻击不到,问地图最多放多少个炮兵。
写了挺久有些细节没注意。。。。首先分析题目,m范围可以进行状态压缩,对单独一个网格进行分析,当是P的时候可以进行放或者不放,放的话我需要考虑上下左右的两个格子都没有被放过炮兵,一开始想以当前格点为右下的顶点最多多少个,状态转移有问题,状态枚举也不能很好预处理。
直接考虑上面两行的状态会简单很多,左右两边如果直接枚举(1<<m)最大的时候写的话一定会T。打表发现一行放炮兵满足两个相互攻击不到最多60个,这么就好做多了。预处理一行可行的状态,记得再对H进行检查,0表示没放 1表示放置了, H的地方不能放置。
dp[i][j][k]:第i行 上一行的状态j 本行状态k。
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
char a[110][12];
int s[63];//两个炮兵相互不攻击的状态
int book[63];//状态中放了多少个炮兵
int n,m;
int dp[105][63][63];//第i行 上一行的状态是j 这一行的状态k 最大炮兵数
inline bool check(int y)
{
int i=0;
while(i<m)
{
if((1<<i)&y)
{
i++;
if(i>=m)
return true;
if(((1<<i)&y))
return false;
i++;
if(i>=m)
return true;
if(((1<<i)&y))
return false;
}
else
i++;
}
return true;
}
inline int solve(int x)
{
int cnt=0;
for(int i=0;i<m;i++)
{
if(x&(1<<i))
cnt++;
}
return cnt;
}
inline bool check1(int x,int y)
{
int cnt=0;
for(int i=m-1;i>=0;i--,cnt++)
{
if(a[x][i]=='H')
{
if(!((1<<cnt)&y))
continue;
else
return false;
}
}
return true;
}
int main()
{
memset(book,0,sizeof(book));
memset(dp,0,sizeof(dp));
int i,j,k;
int len=0;
scanf("%d %d",&n,&m);
for(i=0;i<n;i++)
scanf("%s",a[i]);
for(i=0;i<(1<<m);i++)
{
if(check(i))
{
book[len]=solve(i);
s[len++]=i;
}
}
for(k=0;k<len;k++)
{
if(check1(0,s[k]))
dp[0][0][k]=book[k];
}
for(i=0;i<len;i++)
{
for(j=0;j<len;j++)
{
if((s[i]&s[j])==0&&check1(0,s[i])&&check1(1,s[j]))
dp[1][i][j]=max(book[j]+dp[0][0][i],dp[1][i][j]);
}
}
for(i=2;i<n;i++)//第i行
{
for(j=0;j<len;j++)//第i-1行的状态
{
if((check1(i-1,s[j]))==false)
continue;
for(k=0;k<len;k++)//第i行的状态
{
if((check1(i,s[k]))==false)
continue;
for(int hh=0;hh<len;hh++)//第i-2行的状态
{
if((check1(i-2,s[hh]))==false)
continue;
if((s[j]&s[k])||(s[j]&s[hh])||(s[k]&s[hh]))
{
continue;
}
dp[i][j][k]=max(dp[i][j][k],book[k]+dp[i-1][hh][j]);
}
}
}
}
int ans=0;
for(i=0;i<len;i++)
{
for(j=0;j<len;j++)
{
if(s[i]&s[j])
continue;
ans=max(ans,dp[n-1][i][j]);
}
}
printf("%d",ans);
return 0;
}
题目:click poj-3254
题意:给定一个图,1表示可以放牛,0不行,相邻的格子不能同时放牛,问一共多少种方法。
具体分析跟上面的题目一样,我们只需要知道上一行的状态,预处理出行可行的状态并且检查0是否放牛。
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
const int mod=100000000;
int a[15][15];
int dp[15][400];//第i行 j状态的MAX方法数
int s[400];//一行可存在的状态
int n,m;
inline bool check(int y)
{
int i=0;
while(i<m)
{
if((1<<i)&y)
{
i++;
if(i>=m)
return true;
if(((1<<i)&y))
return false;
}
else
i++;
}
return true;
}
inline bool check1(int x,int y)
{
int cnt=0;
for(int i=m-1;i>=0;i--,cnt++)
{
if(a[x][i]==0)
{
if((1<<cnt)&y)
{
return false;
}
else
continue;
}
}
return true;
}
int main()
{
memset(dp,0,sizeof(dp));
int i,j,k;
int len=0;
scanf("%d %d",&n,&m);
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
cin>>a[i][j];
}
}
for(i=0;i<(1<<m);i++)
{
if(check(i))
{
s[len++]=i;
}
}
for(i=0;i<len;i++)
{
if(check1(0,s[i]))
dp[0][i]=1;
}
for(i=1;i<n;i++)
{
for(j=0;j<len;j++)//本行
{
if((check1(i,s[j]))==false)
continue;
for(k=0;k<len;k++)//上一行行状态
{
if((check1(i-1,s[k]))==false)
continue;
if(s[j]&s[k])
continue;
dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
}
}
}
int ans=0;
for(i=0;i<len;i++)
{
ans+=dp[n-1][i];
ans%=mod;
}
printf("%d",ans);
return 0;
}