想想接触ACM已经一年了,参加ACM也半年了。大四只有少量的课程,一般都准备考研、找工作、实习了。想想我的大学生活俨然已经走过了一半,一年半以来,不说自己大学过得如何,至少做的还不算太差,时间过的真是快啊!想想就算在学校整天玩耍、也没有多少青春可以挥霍了。因此2017年一定要比2016年付出更多的努力才行,任何东西只要肯投入,没有学不会或者做不到的,除非你是无法企及的事。
2017年,相信我会做的更好!
之前队内打算利用空余的时间刷网上的poj分类,所以漫漫的求索路又开始了。
初期的题目做得总是很快的,一下就刷完了,在此做个总结….
(1)、枚举
1、 poj1753Flip Game
题意:一个4*4的方格,每个方格中有一粒棋子,这个棋子一面是白色,一面是黑色。游戏规则为每次任选16颗中的一颗,把选中的这颗以及它四周的棋子一并反过来,当所有的棋子都是同一个颜色朝上时,游戏就完成了。现在给定一个初始状态,要求输出能够完成游戏所需翻转的最小次数,如果初始状态已经达到要求输出0。如果不可能完成游戏,输出Impossible。
分析:方法实在太多,随便暴力都能过,利用位运算枚举16个格子的所有翻转情况,复杂度为O(16*2^16)。高效一点的办法就是BFS求取最小的次数,不过这里可以采用部分枚举的策略。我们的目标是使得所有格子都相同,只要枚举第一行的所有翻转情况,然后依次判断第1行至第3行的每个格子,如果要翻转的话只能通过下面那个格子来解决,最后判断第4行格子是否要翻转即可。当然可以翻转为白色的和黑色的,所以枚举两次就够了。题目还可以采用高斯消元的办法,然而这个不会,从书本上了解到的。。。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 6;
const int INF = 0x3f3f3f3f;
int dx[] = {1, 0, 0, 0}, dy[] = {0, 1, -1, 0};
int tmp[N][N], filed[N][N];
void change(int x, int y) {
for (int i = 0; i < 4; i++) {
int nx = x + dx[i], ny = y + dy[i];
tmp[nx][ny] = !tmp[nx][ny];
}
}
int cal(int f) {
int cnt = 0;
for (int i = 1; i < 4; i++) {
for (int j = 1; j <= 4; j++) {
if (f) {
if (tmp[i][j]) change(i+1, j), cnt++;
}
else {
if (!tmp[i][j]) change(i+1, j), cnt++;
}
}
}
for (int i = 1; i <= 4; i++) {
if (f) {
if (tmp[4][i]) return INF;
}
else {
if (!tmp[4][i]) return INF;
}
}
return cnt;
}
int main() {
for (int i = 1; i <= 4; i++) {
for (int j = 1; j <= 4; j++) {
char c = getchar();
filed[i][j] = c == 'b' ? 1 : 0;
}
getchar();
}
int ans = INF;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 16; j++) {
memcpy(tmp, filed, sizeof(filed));
int t = 0;
for (int k = 0; k < 4; k++)
if (j & (1 << k)) change(1, k+1), t++;
ans = min(ans, t + cal(i));
}
}
ans == INF ? puts("Impossible") : printf("%d\n", ans);
return 0;
}
2、poj2965The Pilots Brothers' refrigerator
题意:一个冰箱上有4*4共16个开关,改变任意一个开关的状态(即开变成关,关变成开)时,此开关的同一行、同一列所有的开关都会自动改变状态。要想打开冰箱,要所有开关全部打开才行。求使得冰箱打开至少要改变开关状态的次数,以及改变哪几个格子。
分析:不是和上题一样了,这次有变化了,没多想,直接暴力过的,利用位运算按次数从小到大枚举翻转的子集。当然还是可以BFS求最小次数。
还有很巧妙的方法,从网上了解到的,如果一个格子为+,那么我们对该格子所在行和列的所有7个格子全部操作一遍,开关本身状态改变了7次,开关同一行、同一列的开关状态改变了4次,其他开关状态改变了2次。(其实也相当于只改变了这一个格子的状态)最后统计所有格子的翻转次数、如果翻转次数为奇数,那么相当于操作一次,翻转偶数次相当于没翻转。这样肯定可以保证次数是最少的。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <utility>
using namespace std;
const int N = 4;
int handle[N][N], cntx[N], cnty[N], vis[N][N];
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
typedef pair<int, int> p;
vector<p> vec;
bool check(int s) {
for (int i = 0; i < 4; i++) cntx[i] = cnty[i] = 0;
memcpy(vis, handle, sizeof(handle));
vec.clear();
for (int i = 0; i < 16; i++)
if (s & (1 << i)) vec.push_back(p(i/4, i%4));
for (int i = 0; i < vec.size(); i++) {
int x = vec[i].first, y = vec[i].second;
cntx[x]++; cnty[y]++;
vis[x][y] = !vis[x][y];
}
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if ((vis[i][j] + cntx[i] + cnty[j]) & 1) return 0;
printf("%d\n", vec.size());
for (int i = 0; i < vec.size(); i++) printf("%d %d\n", vec[i].first+1, vec[i].second+1);
return 1;
}
int main() {
//freopen("in.txt", "r", stdin);
for (int i = 0; i < 4; i++) {
char s[10];
scanf("%s", s);
for (int j = 0; j < 4; j++) handle[i][j] = s[j] == '+' ? 1 : 0;
}
if (!check(0)) {
int flag = 1, up = 1 << 16;
for (int i = 1; i <= 16 && flag; i++) {
int t = (1 << i) - 1;
while (t < up && flag) {
if (check(t)) flag = 0;
int x = t & -t, y = t + x;
t = ((t & ~y) / x >> 1) | y;
}
}
}
return 0;
}
(2)、贪心
1、poj1328
题意:海岸线是一条无限延伸的直线。陆地在海岸线的一侧,而海洋在另一侧。每一个小的岛屿是海洋上的一个点。雷达坐落于海岸线上,只能覆盖d距离,所以如果小岛能够被覆盖到的话,它们之间的距离最多为d。题目要求计算出能够覆盖给出的所有岛屿的最少雷达数目。
分析:首先按坐标排序,然后我们按照从左至右依次覆盖的顺序来求解答案。如何求取最小数目呢?假设从某个小岛开始覆盖、要使得数目最小,当然是要使得一个雷达覆盖尽量多的点,覆盖一个小岛的话,雷达的横坐标只能在x + sqrt(d*d-y*y)的范围内,所以我们采取贪心的策略、尽量地往右移动雷达坐标以覆盖后面的点,所以假设当前的坐标为x + sqrt(d*d-y*y),那么不断地比较,如果x + sqrt(d*d-y*y) > xi - sqrt(d*d-yi*yi)的话证明可以覆盖到它,但是需要注意的一点是当前的坐标要选取到所有岛屿的x + sqrt(d*d-y*y)最小值,这样才不会保证往右移动导致之前的点不在当前的范围内了。采取这样的贪心策略可以保证每次覆盖地点最大化。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 1010;
struct po{
double x, y;
bool operator < (const po& a)const {
return x == a.x ? y < a.y : x < a.x;
}
}p[N];
int main() {
int ca = 0, n;
double d;
while (scanf("%d %lf", &n, &d), n) {
for (int i = 0; i < n; i++) scanf("%lf %lf", &p[i].x, &p[i].y);
sort(p, p+n);
int ans = 1;
double s = d * d;
if (p[0].y > d) ans = -1;
else {
double t = sqrt(s - p[0].y * p[0].y) + p[0].x;
for (int i = 1; i < n; i++) {
if (p[i].y > d) { ans = -1; break; }
double tmp = sqrt(s - p[i].y * p[i].y), sum = p[i].x + tmp;
if (p[i].x - tmp > t) ans++, t = sum;
else t = min(sum, t);
}
}
printf("Case %d: %d\n", ++ca, ans);
}
return 0;
}
2、 poj2