题目是中文的,我就不描述题意了。
题目用到的主要算法是状态压缩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

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150
