poj初期基本算法

想想接触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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值