poj_3185 反转问题
题目大意
有20个碗排成一排,有些碗口朝上,有些碗口朝下。每次可以反转其中的一个碗,但是在反转该碗时,该碗左右两边的碗也跟着被反转(如果该碗为边界上的碗,则只有一侧的碗被反转)。求最少需要反转几次,可以使得所有碗口均朝上。
题目分析
类似黑白染色问题(将一个格子染色,其上下左右四个格子也被染成分别相反的颜色),找到规律之后可以不用进行暴力枚举搜索。
考虑最左边的那个碗,该碗要么被反转要么不被反转,只有两种可能。对于任何一种可能继续考虑:对于碗1,如果碗0口朝下,则只有碗1能够将其反转(因为碗0已经考虑过了),因此碗1必须反转。同样继续考虑碗2(看碗1是否口朝下)....直到最后一个碗。
因此,碗0反转和不反转分别只对应一种20个碗的反转情况。这种看似情况很多的问题,在合理性的剪枝之下,其实合法的状态很少,且合法的状态是可以由开始的状态推出来的。
实现(c++)
#include<stdio.h> #include<string.h> #include<algorithm> #include<cmath> using namespace std; int bowl[25]; int bowl2[25]; int main(){ while (scanf("%d", &bowl[0]) != EOF){ bowl2[0] = bowl[0]; for (int i = 1; i < 20; i++){ scanf("%d", &bowl[i]); bowl2[i] = bowl[i]; } int flip_count = 0; //not flip position 0 for (int i = 1; i < 20; i++){ if (bowl[i - 1]){ flip_count++; bowl[i - 1] = 0; bowl[i] = 1 - bowl[i]; bowl[i + 1] = 1 - bowl[i + 1]; } } int min_flip = flip_count; if (bowl[19] == 1){ min_flip = 10000; } //flip bowl 0 flip_count = 1; bowl2[0] = 1 - bowl2[0]; bowl2[1] = 1 - bowl2[1]; for (int i = 1; i < 20; i++){ if (bowl2[i - 1]){ flip_count++; bowl2[i - 1] = 0; bowl2[i] = 1 - bowl2[i]; bowl2[i + 1] = 1 - bowl2[i + 1]; } } if (bowl2[19] == 0){ min_flip = min_flip < flip_count ? min_flip : flip_count; } printf("%d\n", min_flip); } return 0; }