POJ 1185 解题报告 炮兵阵地
题目是中文的,我就不描述题意了。
题目用到的主要算法是状态压缩dp。
思路是,我们要知道n行最多的炮数,只要知道n-2行所有状态最多的炮数,就可以根据n-1行和n行最多可行的状态算出。也就是说,n-2行以前的炮无论怎样放,都不会影响到第n行炮的放法。
1
2
#include <cstdio>
3
#include <cmath>
4
#include <cstdlib>
5
6
const int large = (int)pow( 2, 10 );
7
int n,m;
8
char map[105][15];
9
int dp[60][60][101];
10
int stack[large];
11
int len = 0;
12
13
//now是当前行,last是上一行,n是行号。其中,now,last都是stack的下标,不是状态
14
int Fun( int now, int last, int n )
15
{
16
int max = 0;
17
int s = 0;
18
int tmp = stack[now]|stack[last]; //已放了炮的列
19
if ( dp[now][last][n] != -1 ) return dp[now][last][n];
20
//统计now状态放了多少炮
21
for ( int i = 0; i < m; ++i )
22
{
23
if ( (stack[now]&(~(1<<i))) != stack[now] ) ++s;
24
}
25
if ( n == 1 ) //结束条件,第一行则返回此行的炮数
26
{
27
dp[now][last][1] = s;
28
return s;
29
}
30
for ( int i = 0; i < len; ++i )
31
{
32
int flag = 1;
33
int s = 0;
34
if ( tmp&stack[i] ) continue; //若相互冲突则不算
35
//若此状态的炮兵放在了山地也不算
36
for ( int j = 0; j < m; ++j )
37
{
38
if ( map[n-2][j] == 'H' && (stack[i]&(~(1<<j))) != stack[i] )
39
{
40
flag = 0;
41
break;
42
}
43
}
44
if ( flag ) max >?= Fun( last, i, n-1 ); //从所有状态里挑最大的保存
45
}
46
max += s; //再加上now这行的炮数
47
dp[now][last][n] = max;
48
return max;
49
}
50
51
//此函数用来判断是不是超出范围
52
bool OK( int a )
53
{
54
if ( a<0 || a>= m ) return false;
55
else return true;
56
}
57
58
int main()
59
{
60
int power;
61
int max = 0;
62
scanf( "%d%d", &n, &m );
63
power = (int)pow( 2, m );
64
65
//第0行初始化为山地
66
for ( int i = 0; i < m; ++i ) map[0][i] = 'H';
67
68
//读入数据储存到map中
69
for ( int i = 1; i <= n; ++i )
70
{
71
scanf( "%s", map[i] );
72
}
73
74
/*在0~power这么多状态中,绝大部分状态是可以舍弃的,因为相邻和隔一个的位置不能同时放炮。我们先把不能出现的状态剔除,将可能出现的状态储存在stack数组里,可以节约时间和空间。*/
75
//初始化stack,test OK
76
for ( int i = 0; i < power; ++i )
77
{
78
int flag = 0;
79
for ( int j = 0; j < m; ++j )
80
{
81
if ( (i&(~(1<<j))) != i )//如果i的j位是1,也就是放炮兵部队
82
{
83
if ( OK(j-1) && (i&(~(1<<(j-1))))!=i ) //j-1位合法且放了炮兵,则不合要求
84
{
85
flag = 1;
86
break;
87
}
88
if ( OK(j-2) && (i&(~(1<<(j-2))))!=i ) //j-1位合法且放了炮兵,则不合要求
89
{
90
flag = 1;
91
break;
92
}
93
if ( OK(j+1) && (i&(~(1<<(j+1))))!=i ) //j-1位合法且放了炮兵,则不合要求
94
{
95
flag = 1;
96
break;
97
}
98
if ( OK(j+2) && (i&(~(1<<(j+2))))!=i ) //j-1位合法且放了炮兵,则不合要求
99
{
100
flag = 1;
101
break;
102
}
103
}
104
}
105
if ( flag == 0 )
106
{
107
stack[len++] = i;
108
}
109
}
110
111
//初始化dp数组
112
for ( int i = 0; i < 60; ++i )
113
{
114
for ( int j = 0; j < 60; ++j )
115
{
116
for ( int k = 0; k < 101; ++k )
117
{
118
dp[i][j][k] = -1;
119
}
120
}
121
}
122
123
//取遍所有本行和相邻的上一行的所有状态,取最大保留。并在传参前确保传入的参数是合法的。
124
for ( int i = 0; i < len; ++i )
125
{
126
for ( int j = 0; j < len; ++j )
127
{
128
int flag = 1;
129
if ( (stack[i]&stack[j]) ) continue; //如果同一列同时有炮则舍弃
130
for ( int k = 0; k < m; ++k )//剔除不合法的情况
131
{
132
if ( map[n][k] == 'H' && (stack[i]&(~(1<<k)))!=stack[i] ) //此状态在山地方了炮
133
{
134
flag = 0;
135
break;
136
}
137
if ( map[n-1][k] == 'H' && (stack[j]&(~(1<<k)))!=stack[j] )
138
{
139
flag = 0;
140
break;
141
}
142
}
143
if ( flag ) max >?= Fun( i, j, n ); //取最大存在max里
144
}
145
}
146
printf( "%d\n", max );
147
148
return 0;
149
}
150

2
#include <cstdio>3
#include <cmath>4
#include <cstdlib>5

6
const int large = (int)pow( 2, 10 );7
int n,m;8
char map[105][15];9
int dp[60][60][101];10
int stack[large];11
int len = 0;12

13
//now是当前行,last是上一行,n是行号。其中,now,last都是stack的下标,不是状态14
int Fun( int now, int last, int n )15
{16
int max = 0;17
int s = 0;18
int tmp = stack[now]|stack[last]; //已放了炮的列19
if ( dp[now][last][n] != -1 ) return dp[now][last][n];20
//统计now状态放了多少炮21
for ( int i = 0; i < m; ++i )22
{23
if ( (stack[now]&(~(1<<i))) != stack[now] ) ++s;24
}25
if ( n == 1 ) //结束条件,第一行则返回此行的炮数26
{27
dp[now][last][1] = s;28
return s;29
}30
for ( int i = 0; i < len; ++i )31
{32
int flag = 1;33
int s = 0;34
if ( tmp&stack[i] ) continue; //若相互冲突则不算35
//若此状态的炮兵放在了山地也不算36
for ( int j = 0; j < m; ++j ) 37
{38
if ( map[n-2][j] == 'H' && (stack[i]&(~(1<<j))) != stack[i] )39
{40
flag = 0;41
break;42
}43
}44
if ( flag ) max >?= Fun( last, i, n-1 ); //从所有状态里挑最大的保存45
}46
max += s; //再加上now这行的炮数47
dp[now][last][n] = max;48
return max;49
}50

51
//此函数用来判断是不是超出范围52
bool OK( int a )53
{54
if ( a<0 || a>= m ) return false;55
else return true;56
}57

58
int main()59
{60
int power;61
int max = 0;62
scanf( "%d%d", &n, &m );63
power = (int)pow( 2, m );64
65
//第0行初始化为山地66
for ( int i = 0; i < m; ++i ) map[0][i] = 'H';67
68
//读入数据储存到map中69
for ( int i = 1; i <= n; ++i )70
{71
scanf( "%s", map[i] );72
}73

74
/*在0~power这么多状态中,绝大部分状态是可以舍弃的,因为相邻和隔一个的位置不能同时放炮。我们先把不能出现的状态剔除,将可能出现的状态储存在stack数组里,可以节约时间和空间。*/75
//初始化stack,test OK 76
for ( int i = 0; i < power; ++i )77
{78
int flag = 0;79
for ( int j = 0; j < m; ++j )80
{81
if ( (i&(~(1<<j))) != i )//如果i的j位是1,也就是放炮兵部队 82
{83
if ( OK(j-1) && (i&(~(1<<(j-1))))!=i ) //j-1位合法且放了炮兵,则不合要求 84
{85
flag = 1; 86
break;87
}88
if ( OK(j-2) && (i&(~(1<<(j-2))))!=i ) //j-1位合法且放了炮兵,则不合要求 89
{90
flag = 1; 91
break;92
}93
if ( OK(j+1) && (i&(~(1<<(j+1))))!=i ) //j-1位合法且放了炮兵,则不合要求 94
{95
flag = 1; 96
break;97
}98
if ( OK(j+2) && (i&(~(1<<(j+2))))!=i ) //j-1位合法且放了炮兵,则不合要求 99
{100
flag = 1; 101
break;102
}103
} 104
}105
if ( flag == 0 )106
{107
stack[len++] = i;108
}109
}110
111
//初始化dp数组112
for ( int i = 0; i < 60; ++i )113
{114
for ( int j = 0; j < 60; ++j )115
{116
for ( int k = 0; k < 101; ++k )117
{118
dp[i][j][k] = -1;119
}120
}121
}122

123
//取遍所有本行和相邻的上一行的所有状态,取最大保留。并在传参前确保传入的参数是合法的。124
for ( int i = 0; i < len; ++i )125
{126
for ( int j = 0; j < len; ++j )127
{128
int flag = 1;129
if ( (stack[i]&stack[j]) ) continue; //如果同一列同时有炮则舍弃130
for ( int k = 0; k < m; ++k )//剔除不合法的情况 131
{132
if ( map[n][k] == 'H' && (stack[i]&(~(1<<k)))!=stack[i] ) //此状态在山地方了炮133
{134
flag = 0;135
break;136
}137
if ( map[n-1][k] == 'H' && (stack[j]&(~(1<<k)))!=stack[j] )138
{139
flag = 0;140
break;141
}142
}143
if ( flag ) max >?= Fun( i, j, n ); //取最大存在max里144
}145
}146
printf( "%d\n", max );147
148
return 0;149
}150




浙公网安备 33010602011771号