算法入门刷题笔记 Day2 K - Coat of Anticubism & L - Five-In-a-Row & M - Island Puzzl......

写在前面

好久没更新公众号和博客了,因为最近在研究新的方向,所以很少发文。
笔者接触编程只有一年,这一年间主要研究启发式算法在运筹学中的应用。但是由于编程基础薄弱,在进一步研究复杂运筹学问题时发现基础算法不过关导致写出的代码运行速度很慢,因此很苦恼。所以决定这个暑假补习一下基础算法,主要是刷一些简单的ACM入门题。偶尔会发一些刷题笔记(偶尔!)。和作者有类似目标的同学可以一起交流共勉!

目前在看的教程:
北京理工大学ACM冬季培训课程

算法竞赛入门经典/刘汝佳编著.-2版可以在这里下载->github

课程刷题点
Virtual Judge

刷题代码都会放在github上,欢迎一起学习进步!

第二章模拟与暴力刷题点,提交密码20202020

就剩5题了,很难受,就早上写了一下写完先。这次AC比较多哈,除了最后一题没有AC(主要懒的写了呜呜呜)。

K - Coat of Anticubism

在这里插入图片描述
cicasso可还行,我还以为自己看错了…

这题应该是数学题…给几条无法组成凸多边形的线段,问新增一条最短长度为多少的线段可以组成凸多边形。其中构成凸多边形时两条线段可以拼接成一条更长的线段(即夹角180°)。

懒得思考数学问题,就直接网上查了一下,发现很多博客题解直接说求三角形,或者语焉不详,我就呵呵。

问题应该就是求三角形,利用两边和大于最长边。但是如何从凸多边形转为三角形呢?思考了一下,类比三角形,凸多边形有这样的性质:最长边小于其他所有边长度之和。(通过划分为多个三角形易证)但是拥有这个性质是否一定能构成凸多边形呢?这个我一下想不出证明,但是我们可以通过拼接线段的方式把这些边拼接成三角形。(不断的把最短的两条边拼成一条边)那么问题就简化为加入一个数,使得除最大数外所有数的和加上这个数 = 原先最大数 + 1。暴力求和即可。

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <string>
#include <set>
using namespace std;

typedef long long LL;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
// #define LOCAL

int numbers[5000005];
// set<int> num;

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
#endif

    int n;
    cin >> n;
    for(int i = 0; i < n; i++)
        cin >> numbers[i];
    sort(numbers, numbers + n);
    int sum = 0;
    for(int i = 0; i < n - 1; i++)
        sum += numbers[i];
    int ans = numbers[n - 1] - sum + 1;
    cout << ans << endl;
    return 0;
}

L - Five-In-a-Row

在这里插入图片描述
五子棋,判断当前情况某方能否一步内获胜。

棋盘只有10*10,选择之间暴力枚举所有空格位置,判断上下左右斜能否连5颗。

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <string>
#include <set>
using namespace std;

typedef long long LL;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
// #define LOCAL

int lenth = 10;
char graph[15][15];

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
#endif

    for (int j = 1; j <= 10; j++)
        graph[0][j] = 'O'; // 'O'是对面的棋子,等同于代表墙体
    for (int i = 1; i <= 10; i++)
    {
        graph[i][0] = 'O';
        for (int j = 1; j <= 10; j++)
            graph[i][j] = '.'; // '.'代表空
        graph[i][10 + 1] = 'O';
    }
    for (int j = 1; j <= 10; j++)
        graph[10 + 1][j] = 'O';

    for (int i = 1; i <= 10; i++)
    {
        string str;
        cin >> str;
        for (int j = 1; j <= 10; j++)
            graph[i][j] = str[j - 1];
    }

    bool flag = false;
    for (int i = 1; i <= 10; i++)
    {
        for (int j = 1; j <= 10; j++)
        {
            if (graph[i][j] != '.')
                continue;
            // 判断左右
            int left = 0, right = 0;
            for (int k = 1;; k++)
            {
                if (graph[i][j - k] == 'X')
                    left++;
                else
                    break;
            }
            for (int k = 1;; k++)
            {
                if (graph[i][j + k] == 'X')
                    right++;
                else
                    break;
            }
            if ((left + right + 1) > 4)
            {
                flag = true;
                goto end;
            }

            // 判断上下
            int up = 0, down = 0;
            for (int k = 1;; k++)
            {
                if (graph[i - k][j] == 'X')
                    down++;
                else
                    break;
            }
            for (int k = 1;; k++)
            {
                if (graph[i + k][j] == 'X')
                    up++;
                else
                    break;
            }
            if ((up + down + 1) > 4)
            {
                flag = true;
                goto end;
            }

            // 判断左侧斜向上、下
            up = 0, down = 0;
            for (int k = 1;; k++)
            {
                if (graph[i - k][j + k] == 'X')
                    down++;
                else
                    break;
            }
            for (int k = 1;; k++)
            {
                if (graph[i + k][j - k] == 'X')
                    up++;
                else
                    break;
            }
            if ((up + down + 1) > 4)
            {
                flag = true;
                goto end;
            }

            // 判断右侧斜向上、下
            up = 0, down = 0;
            for (int k = 1;; k++)
            {
                if (graph[i - k][j - k] == 'X')
                    down++;
                else
                    break;
            }
            for (int k = 1;; k++)
            {
                if (graph[i + k][j + k] == 'X')
                    up++;
                else
                    break;
            }
            if ((up + down + 1) > 4)
            {
                flag = true;
                goto end;
            }
        }
    }

end:
    if (flag)
        cout << "YES";
    else
        cout << "NO";
    return 0;
}

M - Island Puzzle

在这里插入图片描述
一个循环的队列,其中有一个空位,每次只能向空的位置移动一步,问给出两个队列是否可以相互转换。

去掉空位的0后判断循环是否相等。判断是否相等的时候,可以先找到一个相等的数,再往后判断。

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <string>
#include <set>
#include <queue>
using namespace std;

typedef long long LL;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
//#define LOCAL

int x[200005];
int y[200005];

void swap(int& x, int& y)
{
    int temp = x;
    x = y;
    y = temp;
}

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
#endif
    int n;
    cin >> n;
    int m = 0;
    for (int i = 0; i < n; i++)
    {
        int temp;
        cin >> temp;
        if (!temp)
            continue;
        else
            x[m++] = temp;
    }
    m = 0;
    for (int i = 0; i < n; i++)
    {
        int temp;
        cin >> temp;
        if (!temp)
            continue;
        else
            y[m++] = temp;
    }

    bool flag = true;
    int k = 0;
    while (x[k] != y[0])
    {
        k++;
    }
    n--;
    for (int j = 0; j < n; j++)
    {
        if (x[(k++) % n] != y[j]) {
            flag = false;
            break;
        }
    }

    if (flag)
        cout << "YES";
    else
        cout << "NO";
    return 0;
}

N - Fractions Again?!

在这里插入图片描述
1/k=1/x+1/y1/k = 1/x + 1/y的x和y。

只需要遍历x,设x为较小的数,遍历时从k+1到2k即可。同分之后算y,不然精度可能有问题。

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <string>
#include <set>
#include <math.h>
using namespace std;

typedef long long LL;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
// #define LOCAL

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
#endif

    int n;
    while (cin >> n)
    {
        int sum = 0;
        vector<pair<int, int>> num_pair;
        for (int i = n + 1; i <= 2 * n; i++)
        {
            if (!(n * i % (i - n)))
            {
                int j = n * i / (i - n);
                num_pair.push_back(make_pair(j, i));
            }
        }
        cout << num_pair.size() << endl;
        for (vector<pair<int, int>>::iterator it = num_pair.begin(); it != num_pair.end(); it++)
        {
            printf("1/%d = 1/%d + 1/%d\n", n, (*it).first, (*it).second);
        }
    }

    return 0;
}

O - The Answer to the Ultimate Question of Life, The Universe, and Everything.

在这里插入图片描述
这个题目比较有意思,题目头就很有意思,做法也很有意思。

T[0,10]x,x[0,200],a3+b3+c3=xa,b,c,a,b,c[5000,5000]给定T\in[0, 10]个x, x \in [0, 200], 分别求满足a^3 + b^3+c^3 = x的a, b, c, 其中a, b, c \in [-5000, 5000]。

要遍历两次-5000——5000,这里就已经10e8了,基本超出时间限制。我尝试着写了一下,加了一点优化,比如试了一下预处理立方(可能负优化了),某一个数限定为正数(x > 0),但都不行。看AC的答案发现所有人都是写了一个表:
在这里插入图片描述
我感到很吃惊。上网一查才知道,原来打表是一种方法,因为x的范围比较小,直接在本地找出所有x的解然后打表上交。果然神奇。

具体方法可以参考这篇博文,详细介绍了本地打表的代码(有些博文直接写上交的代码,我不知道作者在想些啥):

https://blog.csdn.net/qq_44691917/article/details/103588685

这里就不放我写的了,简单介绍一下博文里的打表思路。

首先200次询问肯定要预处理立方;

算立方根代码里用遍历的方式找三次方,可能效果好一点;

用一个unordered_map,unordered_map是未排序的map,因为内部实现了哈希表,因此查找速度比较快,mark一下下次试试;

肯定有一个正数,因此第一次遍历0-5000;

第二次遍历也可以遍历0-5000,只需要查找的时候查找两次;

下面放下别人的代码(据说138s解决):

#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef long long ll;
tr1::unordered_map<ll,int> f;

ll g(ll x){
	for(ll i=-5000;i<=5000;i++)
		if (i*i*i==x) return i;

}

bool find(int x){
	for(ll i =0;i<=5000;i++)
			for(long long j=0;j<=5000;j++)
				if (f.count(i*i*i+j*j*j+x)){
					printf("a[%d] = %lld;\n",x,g(i*i*i+j*j*j+x));
					printf("b[%d] = %lld;\n",x,-i);
					printf("c[%d] = %lld;\n",x,-j);
					return true;
				}else if (f.count(i*i*i+j*j*j-x)){
					//printf("%lld %lld %lld = %d\n",i,j,-g(i*i*i+j*j*j-x),x);
					printf("a[%d] = %lld;\n",x,g(i*i*i+j*j*j-x)==0?0:-g(i*i*i+j*j*j-x));
					printf("b[%d] = %lld;\n",x,i);
					printf("c[%d] = %lld;\n",x,j);
					return true;
				}
	return false;
}

int main()
{
	freopen("ans.txt","w",stdout);
	f.clear();
	for(ll i=-5000;i<=5000;i++)
        f[i*i*i] = 1;
	for(int x=0;x<=200;x++){
		//printf("find %d\n",x );
		if (find(x)){
			printf("vis[%d] = true;\n",x);
		}
		else
			printf("vis[%d] = false;\n",x);
	}
	return 0;
}


结语

模拟暴力结束了…然而紫书里还有一章讲模拟暴力…紫书难度比较大,感觉比这个要难,我决定紫书还是慢慢刷吧…下次把紫书上的STL部分刷一点先。

posted @ 2020-07-07 13:10  舟寒丶  阅读(48)  评论(0编辑  收藏  举报