2015-08-21
问题是中文的,大家可以进去看看。
先说一个坑,这个问题我交了很多次,都没过,反正是WA到我烦了,都不知道哪里错了!!!怎么会有错,然后翻了一下别人的代码,立马懂了
原因就是这题是因为他输入是字符,我一般清字符就直接用一个fflush(stdin)就完事了,上次石子游戏就坑了我一次,所以我这次没用fflush了,因为fflush会清掉整个缓冲区,而在线提交的原理就是把一大堆测试数据放缓冲区一个一个读的,真是太坑了,我以为在线编译器会直接忽略\n的,然而显然它没有
OK我们回到这一题上来,这一题和我上一次写过的稻田养牛(点我查看)比较像,那么区别在哪里呢?嗯,区别就在这一题要存两个位置,因为炮兵可以打到两个区域,而且左右的判断规则也不同,并且这一题是要算的最大数量(而不是组合数)。嗯。
那么我们现在要想的问题就是怎么保存两个位置就好了,并且这两行的信息必须集中到一起(不然怎么叫状态压缩呢),所以我们立马就想到了用一个二维数组。同时,因为这题是算的最大数量,我们可以规定一个“不合法位置”为-1,来简化判断条件(这样就不用再和图的上一行和上上一行的P,H位置进行比较了,比较方便)。
那么,最后我们得出的二维数组将会蕴含下列信息:
1.至上一行为止的每一个状态所对应的最大炮兵数
2.并且上一行与上上一行肯定满足所有的对应限制(不然肯定会显示-1非合法位置)
所以,我们现在只用本行和上上一行的炮兵满足不在一列的要求就可以了,也是蛮方便的。
状态转移方程:Now_State[j][k]=max(Now[j][k],Prev[k][h]+State_men[j]);很简单
这个题的确比稻田那个要难一点,主要是两行情况要注意一下,而且还要算炮兵数(这个有一个技巧,在注释写了)
#include <stdio.h> #include <stdlib.h> #include <string.h> void Search(const int, const int, int *const); void Inivilize_Valid_State(const int, int *const, int **, int **); void ReMark(int **,const int); int **Inivilize_Sum_State(const int); static int If_Valid(const int, const int); static int Cal_Men_Of_State(const int); static int max_(const int, const int); int main(void) { int M, N, i, j, *line = NULL; char tmp; while (scanf("%d%d", &N, &M) != EOF) { //H的比特位就是1,P就是1 line = (int *)malloc(sizeof(int)*N); for (i = 0; i < N; i++) { getchar(); line[i]= 0; for (j = 0; j < M; j++) { tmp = getchar(); if (tmp == ‘H‘) line[i] |= (1 << (M - j - 1)); } } Search(N, M, line); free(line); } return 0;; } static int If_Valid(const int state1, const int state2) { /*Name: If_Valid *Function: 判断相邻两行之间是否违反了炮兵不能在同一列的规定(或者隔行) 判断是否违反了H位置不能放炮兵的规定 *Return: 合法返回1,不合法返回0 */ if (state1&state2) return 0; return 1; } void Inivilize_Valid_State(const int n, int *const valid_state_sum, int **Valid_State, int **State_men) { /*Name: Inilivilize: *Function: 遍历每一行可能的组合方式,找到可以放炮兵的那些合法位置,存起来 *return: 无 */ *valid_state_sum = 0; *Valid_State = (int *)malloc(sizeof(int)*n); *State_men = (int *)malloc(sizeof(int)*n); int i; for (i = 0; i < n; i++) { if ((!(i&(i << 1))) && (!(i&(i << 2)))){ //这个表示当一个位置是1时,其左右的两个位置都必须是空位置才行 //比如100100这个就是合法位置,而110100这些就不行 (*Valid_State)[(*valid_state_sum)] = i; (*State_men)[(*valid_state_sum)++] = Cal_Men_Of_State(i); } } } static int Cal_Men_Of_State(const int i) { /*Name: Cal_Men_Of_State *Function: 计算每一个合法位置的1的个数(也就是放炮兵的个数) *返回值: 这个合法位置的炮兵的个数 */ int k = i, sum_men = 0; while (k) { //这个位运算的意思是去掉所有的1,然后统计去掉的1的数量 //比如1010,-1以后变成1001,然后再和1010按位与,就得出了1000,去了一个1 //1000再-1变0111,按位与变0000,又去掉一个1,然后k=0,退出循环 sum_men++; k &= (k - 1); } return sum_men; } inline int **Inivilize_Sum_State(const int valid_state_sum) { /*Name: Inivilize_Sum_State *Function: 初始化Now Prev位置 *Return: 返回对应地址 */ int **tmp = (int **)malloc(sizeof(int)*valid_state_sum); int i; for (i = 0; i < valid_state_sum; i++) { tmp[i] = (int *)malloc(sizeof(int)*valid_state_sum); memset(tmp[i], (int)-1, sizeof(int)*valid_state_sum); } return tmp; } void ReMark(int **tmp, const int valid_state_sum) { /*Name: ReMark *Function: 初始化Now位置 *Return: 返回对应地址 */ int i; for (i = 0; i < valid_state_sum; i++) memset(tmp[i], (int)-1, sizeof(int)*valid_state_sum); } static int max_(const int a, const int b) { return a > b ? a : b; } void Search(const int line, const int col, int *const Gragh) { /*Name: Search *Function: 得出最大炮兵数量 *Return: 无 */ int valid_state_sum, i, j, k, h, max; int *State_men = NULL, *Valid_State = NULL; Inivilize_Valid_State((1 << col), &valid_state_sum, &Valid_State, &State_men); //注意(1<<col)是表示有多少列就有2^col个比特位的组合哦 int **Now_State = Inivilize_Sum_State(valid_state_sum); int **Prev = Inivilize_Sum_State(valid_state_sum); int **ec = NULL; for (i = 0; i < valid_state_sum; i++) { if (If_Valid(Gragh[0], Valid_State[i])) Now_State[i][0] = State_men[i]; } ec = Prev; Prev = Now_State; Now_State = ec; ReMark(Now_State, valid_state_sum); for (i = 1; i < line; i++) { for (j = 0; j < valid_state_sum; j++) { if (If_Valid(Gragh[i], Valid_State[j])) { for (k = 0; k < valid_state_sum; k++) { if (!(Valid_State[k] & Valid_State[j])) { for (h = 0; h < valid_state_sum; h++) { if (!(Valid_State[h] & Valid_State[j]) && Prev[k][h] != -1) Now_State[j][k] = max_(Now_State[j][k], Prev[k][h] + State_men[j]); } } } } } ec = Prev; Prev = Now_State; Now_State = ec; ReMark(Now_State, valid_state_sum); } for (i = 0, max = 0; i < valid_state_sum; i++) for (j = 0; j < valid_state_sum; j++) max = max_(Prev[i][j], max); printf("%d\n", max); for (i = 0; i < valid_state_sum; i++) free(Now_State[i]); for (j = 0; j < valid_state_sum; j++) free(Prev[j]); free(Now_State); free(Prev); }
最后说一下,如果这道题要你算组合数,那么就不能标记-1为不合理位置了,那必须上一行和上上一行要和PH位置进行两次对比,然后还要上一行和上上一行不产生冲突才行
另外这题WA真是太恶心了,搞得我最近几天都不想写代码了,真是醉