算法入门刷题笔记 Day5 - G - Voting -- L - Mahmoud and a Triangle

写在前面

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

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

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

课程刷题点
Virtual Judge

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

G - Voting

在这里插入图片描述
题目我没有搞清楚,好像是类似暗杀、禁言的操作…搜了下代码好像也没啥,就过了。

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

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, int> pdi;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
// #define LOCAL

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

const int maxn = 200000 + 5;

char a[maxn];
int vis[maxn];

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
	// freopen("data.out", "w", stdout);
#endif
	int n;
	while (cin >> n)
	{
		memset(vis, 0, sizeof(vis));
		scanf("%s", a);
		queue<int> d;
		queue<int> r;
		for (int i = 0; i < n; i++)
			if (a[i] == 'D')
				d.push(i);
			else
				r.push(i);
		while (!d.empty() && !r.empty())
		{
			for (int i = 0; i < n; i++)
			{
				if (vis[i])
					continue;
				if (a[i] == 'D')
				{
					int tmpd = d.front();
					d.pop();
					int tmpr = r.front();
					r.pop();
					vis[tmpr] = 1;
					d.push(tmpd);
				}
				else
				{
					int tmpr = r.front();
					r.pop();
					int tmpd = d.front();
					d.pop();
					vis[tmpd] = 1;
					r.push(tmpr);
				}
				if (d.empty() || r.empty())
					break;
			}
		}
		if (!d.empty())
			printf("D");
		else
			printf("R");
	}
	return 0;
}

H - New York Hotel

在这里插入图片描述
在二维坐标平面内给出一组点A,一组点B,问A中所有点到B中某个点的最大曼哈顿距离的最小值是多少。

所谓曼哈顿距离,指的是只能想左右上下走的两点间距离。

直接枚举所有两点间距离太慢了,达到了1e10。通常计算曼哈顿距离有如下方法(摘抄自https://www.cnblogs.com/orangee/p/10182886.html):

对于P,Q两点,曼哈顿距离|Px-Qx|+|Py-Qy|可看作(±Px±Py)-(±Qx±Qy),不难发现Px应该与Qx的符号相同,Py与Qy符号相同,因此共四种情况。这样写的好处是,每个点可以表示成相同的形式(±Px±Py)。而曼哈顿距离一定是四种情况中值最大的那种,所以要求两点最远曼哈顿距离,可以枚举所有的取符号情况,对于每种情况,维护出上述表示下n个点的最大值与最小值,求出差值。则最远的曼哈顿距离一定是所有情况中的最大差值。

因此第一次循环A组时可以分别保存四种情况的最大值,X+Y,X-Y,-X+Y,-X-Y,然后循环B组时对应计算曼哈顿距离,即可降低复杂度。

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

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, int> pdi;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
// #define LOCAL

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

const int maxn = 200000 + 5;

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
	// freopen("data.out", "w", stdout);
#endif
	int n, m;
	scanf("%d %d", &n, &m);
	int hotel, restaurant;
	scanf("%d", &hotel);
	ll tmp1, tmp2;

	ll ff, zz, fz, zf;
	scanf("%lld %lld", &tmp1, &tmp2);
	ff = -tmp1 - tmp2;
	zz = tmp1 + tmp2;
	fz = -tmp1 + tmp2;
	zf = tmp1 - tmp2;
	for (int i = 1; i < hotel; ++i)
	{
		scanf("%lld %lld", &tmp1, &tmp2);
		ff = max(ff, -tmp1 - tmp2);
		zz = max(zz, tmp1 + tmp2);
		fz = max(fz, -tmp1 + tmp2);
		zf = max(zf, tmp1 - tmp2);
	}

	scanf("%d", &restaurant);
	ll result = INT_MAX;
	int flag;
	for (int i = 0; i < restaurant; ++i)
	{
		scanf("%lld %lld", &tmp1, &tmp2);
		ll tmp = max(max(tmp1 + tmp2 + ff, tmp1 - tmp2 + fz), max(-tmp1 + tmp2 + zf, -tmp1 - tmp2 + zz));
		if (tmp < result)
		{
			result = tmp;
			flag = i + 1;
		}
	}
	printf("%lld\n%d\n", result, flag);
	return 0;
}

I - Lesha and array splitting

在这里插入图片描述
给一组数,问能否按顺序划分成若干个和非0的集合,输出集合对应坐标范围。若无法划分输出NO。

非0的数可以单独作为一个集合,那么问题就好办了,对0特殊处理,如果前面有集合,直接加入前面的集合,若0是第一个集合,向后找到非0的数。

再第一次循环读取时判断是否有非0的数,若全都是0则无法划分。

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

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, int> pdi;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
// #define LOCAL

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

const int maxn = 100 + 5;
int num[maxn];
pii area[maxn];

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
	// freopen("data.out", "w", stdout);
#endif
	int n, cnt = 0, flag = 0;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		cin >> num[i];
		if(num[i])
			flag = 1;
	}
	if(!flag)
	{
		cout << "NO";
		return 0;
	}

	for (int i = 0; i < n; i++)
	{
		if (num[i])
		{
			area[cnt].first = area[cnt].second = i;
			cnt++;
		}
		else
		{
			if (cnt > 0)
				area[cnt - 1].second++;
			else
			{
				area[0].first = 0;
				for (i = i + 1; i < n; i++)
				{
					if (num[i])
					{
						area[0].second = i;
						cnt++;
						break;
					}
				}
			}
		}
	}
	cout << "YES" << endl;
	cout << cnt << endl;
	for (int i = 0; i < cnt; i++)
		printf("%d %d\n", area[i].first + 1, area[i].second + 1);
	return 0;
}

J - Bash’s Big Day

在这里插入图片描述
给一组数,问最多能挑出多少个数,使得其中的数最大公约数不为0。

第一想法是对每个数质因数分解,统计所有质数在这组数中出现的次数(在一个数中出现多次也记为+1),最大数字即为答案。

编写程序后发现TLE!

搜了一下,发现不需要质因数分解,只需要因数分解即可。事实上质因数也会被分解出来,所以也达到了对应效果。

我为了防止质因数过少,将数组的桶改为了map,质因数分解时用了set,不知道是不是导致速度变慢的原因。但我还是觉得我的第一个想法比较艺术(doge),所以还是放我自己的代码。

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

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, int> pdi;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
// #define LOCAL

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

const int maxn = 100000 + 5;
map<int, int> primes;
void gcd_split(int n)
{
	set<int> p;
	for (int i = 2; i <= n; i++)
	{
		while (n != i)
		{
			if (n % i == 0)
			{
				p.insert(i);
				n = n / i;
			}
			else
				break;
		}
	}
    p.insert(n);
	for(set<int>::iterator it = p.begin(); it != p.end(); it++)
		primes[*it]++;
}

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
	// freopen("data.out", "w", stdout);
#endif
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		int temp;
		cin >> temp;
		gcd_split(temp);
	}
	int ans = 0;
	for(map<int, int>::iterator it = primes.begin(); it != primes.end(); it++)
		if(it->second > ans) ans = it->second;
	cout << ans;
	return 0;
}

K - USB vs. PS/2

在这里插入图片描述
一堆鼠标有两种接口,U和P,有三种电脑,分别是只有U接口的,只有P接口的和UP都有的。

一堆不同类型鼠标分别有对应价格、数量,给一堆三种电脑,要求尽量多购买适合三种电脑的鼠标,第二目标是价格最低,要求输出购买的鼠标数量、总价格。

对价格进行排序,按贪心思想先安排只有U接口、P接口的电脑,UP都行的尽量买价格低的。注意不能确定电脑数量和鼠标数量的关系,要全面考虑哪种较多的情况。

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

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, int> pdi;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
// #define LOCAL

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

vector<ll> usb, ps2;

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
	// freopen("data.out", "w", stdout);
#endif
	int n, nu, np, nb;
	scanf("%d%d%d%d", &nu, &np, &nb, &n);
	for (int i = 0; i < n; i++)
	{
		ll price;
		string type;
		cin >> price >> type;
		if (type[0] == 'U')
			usb.push_back(price);
		else
			ps2.push_back(price);
	}
	sort(usb.begin(), usb.end());
	sort(ps2.begin(), ps2.end());
	ll sum = 0;
	int cnt = 0;
	for (int i = 0; i < nu && i < usb.size(); i++)
	{
		sum += usb[i];
		cnt++;
	}
	for (int i = 0; i < np && i < ps2.size(); i++)
	{
		sum += ps2[i];
		cnt++;
	}
	for (int i = 0; i < nb; i++)
	{
		if (nu < usb.size() && np < ps2.size())
		{
			if (usb[nu] > ps2[np])
				sum += ps2[np++];
			else
				sum += usb[nu++];
			cnt ++;
		}
		else if(nu < usb.size())
		{
			sum += usb[nu++];
			cnt++;
		}
		else
		{
			sum += ps2[np++];
			cnt++;
		}
	}
	printf("%d %lld", cnt, sum);
	return 0;
}

L - Mahmoud and a Triangle

在这里插入图片描述
给一堆已知长度的边,问能否构成三角形。

排序,按顺序三个三个对比。

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

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, int> pdi;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
// #define LOCAL

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

const int maxn = 100000 + 5;
ll arc[maxn];

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
	// freopen("data.out", "w", stdout);
#endif
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
		cin >> arc[i];
	sort(arc, arc + n);
	bool ans = false;
	ll a1 = arc[0], a2 = arc[1], a3 = arc[2];
	if (a1 + a2 > a3)
	{
		ans = true;
		goto end;
	}
	for (int i = 3; i < n; i++)
	{
		a1 = a2;
		a2 = a3;
		a3 = arc[i];
		if (a1 + a2 > a3)
		{
			ans = true;
			break;
		}
	}
	end:
	if (ans)
		cout << "YES" << endl;
	else
		cout << "NO" << endl;
	return 0;
}

结语

这部分题比较简答,休闲。明天起回头做一下拓扑排序和最短路,然后看紫书。

最近要做下运筹相关内容,刷题随心情。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值