POJ 1185 解题报告 炮兵阵地
题目是中文的,我就不描述题意了。
题目用到的主要算法是状态压缩dp。
思路是,我们要知道n行最多的炮数,只要知道n-2行所有状态最多的炮数,就可以根据n-1行和n行最多可行的状态算出。也就是说,n-2行以前的炮无论怎样放,都不会影响到第n行炮的放法。
1
2#include <cstdio>
3#include <cmath>
4#include <cstdlib>
5
6const int large = (int)pow( 2, 10 );
7int n,m;
8char map[105][15];
9int dp[60][60][101];
10int stack[large];
11int len = 0;
12
13//now是当前行,last是上一行,n是行号。其中,now,last都是stack的下标,不是状态
14int 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//此函数用来判断是不是超出范围
52bool OK( int a )
53{
54 if ( a<0 || a>= m ) return false;
55 else return true;
56}
57
58int 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
6const int large = (int)pow( 2, 10 );
7int n,m;
8char map[105][15];
9int dp[60][60][101];
10int stack[large];
11int len = 0;
12
13//now是当前行,last是上一行,n是行号。其中,now,last都是stack的下标,不是状态
14int 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//此函数用来判断是不是超出范围
52bool OK( int a )
53{
54 if ( a<0 || a>= m ) return false;
55 else return true;
56}
57
58int 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