POJ1185 炮兵阵地 和 POJ2411 Mondriaan's Dream
炮兵阵地
Language:
炮兵阵地
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 34008 | Accepted: 13083 |
Description
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
Input
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
Output
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4 PHPP PPHH PPPP PHPP PHHP
Sample Output
6
Source
分析
由于会影响两行,所以需要在行数以外增加两维状态。
预处理出单行的合法状态,大概在60左右,然后枚举转移即可。
时间复杂度\(o(n 60^3)\),由于地形的因数,状态量又会减少,上界很松。
#include<iostream>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using std::max;
int dp[101][77][77];
int sg[101];
int n,m,idx,s[77],cnt0[77];
int get_one(int x){
int cnt=0;
while(x) x&=x-1,++cnt;
return cnt;
}
bool ok(int x){
return x&x<<1||x&x<<2?0:1;
}
void init(){
for(int i=0;i<1<<m;++i)if(ok(i)){
s[idx]=i,cnt0[idx++]=get_one(i);
}
}
bool valid(int i,int x){
return sg[i]&x?0:1;
}
int solve(){
int ans=0;
memset(dp,-1,sizeof dp);
dp[0][0][0]=0;
for(int i=0;i<idx;++i)if(valid(1,s[i])){
dp[1][i][0]=cnt0[i];
ans=max(ans,dp[1][i][0]);
}
for(int i=2;i<=n;++i)
for(int j=0;j<idx;++j)if(valid(i,s[j]))
for(int k=0;k<idx;++k)if(valid(i-1,s[k])&&(s[j]&s[k])==0){
int last=0;
for(int l=0;l<idx;++l)if(dp[i-1][k][l]!=-1&&(s[l]&s[j])==0)
last=max(last,dp[i-1][k][l]);
dp[i][j][k]=max(dp[i][j][k],last+cnt0[j]);
if(i==n) ans=max(ans,dp[i][j][k]);
}
return ans;
}
int main(){
// freopen(".in","r",stdin),freopen(".out","w",stdout);
read(n),read(m);
for(int i=1;i<=n;++i){
static char s[11];
scanf("%s",s);
for(int j=0;j<m;++j)
if(s[j]=='H') sg[i]|=1<<j;
}
init();
printf("%d\n",solve());
return 0;
}
Mondriaan's Dream
Language:
Mondriaan's Dream
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 21909 | Accepted: 12282 |
Description
Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series' (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.
Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!
Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!
Input
The input contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.
Output
For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical tilings multiple times.
Sample Input
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0
Sample Output
1 0 1 2 3 5 144 51205
Source
分析
网上一些题解都是什么鬼,轮廓线、插头DP都冒出来了。
用\(F[i,j]\)表示前\(i\)行,第\(i\)行的放置状态为\(j\)的方案数,把特殊的\(1\times 2\)矩形的上半部分的状态定为1。
这样\(j\)能转移到\(k\),当且仅当
- \(j\&k=0\),显然的。
- \(j|k\)的每一段连续的0的数量必须有偶数个,这体现了\(2\times 1\)矩形的要求。
预处理出那些状态转移合法,大力转移即可。
时间复杂度\(O(n 2^{2m})\)
#include<iostream>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
int n,m;
ll f[12][1<<11];
bool in_s[1<<11];
int main(){
// freopen(".in","r",stdin),freopen(".out","w",stdout);
while(read(n)|read(m)){
for(int i=0;i<1<<m;++i){
bool cnt=0,has_odd=0;
for(int j=0;j<m;++j){
if(i>>j&1) has_odd|=cnt,cnt=0;
else cnt^=1;
}
in_s[i]=has_odd|cnt?0:1;
}
f[0][0]=1;
for(int i=1;i<=n;++i)
for(int j=0;j<1<<m;++j){
f[i][j]=0;
for(int k=0;k<1<<m;++k)
if((j&k)==0&&in_s[j|k])
f[i][j]+=f[i-1][k];
}
printf("%lld\n",f[n][0]);
}
return 0;
}
静渊以有谋,疏通而知事。