NEUQ-ACM预备队-week17

507 Lusir的游戏

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/674
//这里首先思考讲初始能量值设为0 一次一次越过建筑
//分别计算出每次越过建筑时的能力值 进行一次排序后 取最小能量值的相反数
//如果最小能量值依然大于等于0 则初始能量值就设置为0 
//发现如果设初始能量值为0的话,每一次都会减少能量值而不会增加能量值
//但是这个基础的思路是一个O(n)的循环
//是否可以基于这个O(n)的循环 设置一个二分check呢?
int n;
int h[maxn], ans[maxn];
int maxx = -1e9, minn = 1e9;
bool check(int E)
{
	for (int i = 1; i <= n; i++)
	{
		E = E + E- h[i];
		if (E < 0)
		{
			return false;
		}
		if (E > maxx) return true;
	}
	if(E>=0) return true;
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &h[i]);
		maxx = max(maxx, h[i]);
		minn = min(minn, h[i]);
	}
		
	int l = minn, r = maxx;
	while (l < r)
	{
		int mid = (l + r) / 2;
		if (check(mid))
		{
			r = mid;
		}
		else
		{
			l = mid+1;
		}
	}
	printf("%d", l);
	return 0;
}

603 整除光棍

本题贴心地指出数据会变为高精度数 笔者猜测可以使用高精度数的模板直接AC~~ 但是由于笔者是一名蒟蒻~~,并不会使用高精度数的模板

所以进行了以下的思考 具体就见代码吧

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/676
//由于本题给定的被除数字是确定的“11111....”序列
//因此在模拟高精度数字的除法时直接赋值为1111..即可
//而判断是否为整除 只需要在原本n位的数字下多增加一个n-1位 可以认为是
//小数位 模拟到最后小数位不为0的话 表明没有整除 需要继续增加序列的长度
//同时不断用ans记录得到的商 并删去前导0即可
int maxn = 1e7 + 2;
int x;
string ans;
string s = "1";
bool check()
{
	vector<int>a(s.size()+1);
	for (int i = 0; i <a.size()-1; i++)
		a[i] = 1;
	for (int i = 0; i < a.size() - 1; i++)
	{
		ans += a[i] / x + 48;
		a[i + 1] += (a[i] % x)*10;
	}
	if (a[a.size() - 1] != 0)return false;
	else return true;
}
int main()
{
	cin >> x;
	while (!check())
	{
		s += '1';
		ans.clear();
	}
	while (*ans.begin() == '0')
	{
		ans.erase(ans.begin());
	}

	cout << ans << " " << s.size();
	return 0;
}

604 碰撞2

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/705
//这道碰撞题并不需要求多少次碰撞次数
//简单思考一下 只要在同一行中 存在两个相向而行的人 并且向右行的横坐标
//小于向左行的横坐标即可
//而且 因为人们可以无限制地走
//我们不需要保存全部在这一行的人 我们只需要保存向右行的最左人和向左行
//的最右人 如果连这两个人都无法发生碰撞 那么在这一行就不可能有人
//会碰撞
const int maxn = 2e5 + 1;
int n;
string s=" ";
//分别表示右行最左人和左行最右人 first表示y second 表示x 用y做KEY
map<int, int>l, r;
int main()
{
	scanf("%d", &n);
	//p->first————x p->second————y
	vector<pair<int,int> >p(n+1);
	for (int i = 1; i <= n; i++)
	{
		int a, b;
		scanf("%d%d", &a, &b);
		p[i].first = a;
		p[i].second = b;
	}
	string temp;
	cin >> temp;
	s += temp;
	for (int i = 1; i <= n; i++)
	{
		//向右行 更新右行最左人 如果此时在该纵坐标下已经有人
		//更新为向右走的最小坐标值
		//如果没人 那就把这个人放入
		if (s[i] == 'R')
		{
			if (l.count(p[i].second))
			{
				l[p[i].second] = min(l[p[i].second], p[i].first);
			}
			else
				l[p[i].second] = p[i].first;
		}
		if (s[i] == 'L')
		{
			if (r.count(p[i].second))
			{
			  //注意左行最右人的横坐标应该是最大值 用max 这里没发现 搞了好久 悲伤
				r[p[i].second] = max(r[p[i].second], p[i].first);
			}
			else
				r[p[i].second] = p[i].first;
		}
	}
	bool flag = true;
	for (auto i = l.begin(); i != l.end(); i++)
	{
		//如果在该纵坐标下没有向左最右人 直接进入下一个纵坐标的判断
		if (!r.count(i->first))
			continue;
		if (i->second < r[i->first])
		{
			printf("Yes");
			flag = false;
			break;
		}
	}
	if (flag)
		printf("No");
	return 0;
}

除了代码所示的思考 还有使用map的优势

在本题中 如果对纵坐标1e9的数字使用数组去存储每一行的情况 很显然数组是过大的 所以用map去存这些看起来是离散的数字

其次 map在对应行/列寻找左行最右人和右行最左人都具有快速搜索的优势

606 巨大的牛棚

本题主要是用到了二维前缀和 这个算法在这道题里具有相当的优越性!

不过本题的数据也并不是很大 二分或者枚举都可以解决

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/671
//由于牛棚要求为严格的正方形
//这里使用二维前缀和去判断每一个左上角延申的最大不含右下角的值即可
//并且由于数据最大为1k 个人感觉O(N^2)的复杂度也不会超时
//
//错误 即便规定牛棚的形状是规则的正方形 也并不代表牛棚只能从
//(1,1)-(2,2)-(3,3) 事实上只要是横纵坐标增长相同就可以是正方形
//也就是说时间复杂度并不是简单的O(N^2) 要遍历完全部的点是O(N^3)的
//时间复杂度
//不过看起来也还是可以AC 这里也可以用二分的办法去找牛棚的长度
//也是可以的
const int maxn = 1e3 + 1;
int n,t;
bool vis[maxn][maxn];
int Matrix[maxn][maxn];
int ans[maxn][maxn];
int main()
{
	scanf("%d%d", &n,&t);
	while (t--)
	{
		int a, b;
		scanf("%d%d", &a, &b);
		vis[a][b] = true;
	}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
		{
			Matrix[i][j] = Matrix[i - 1][j] + Matrix[i][j - 1] - Matrix[i - 1][j - 1] + vis[i][j];
		}
	int maxx = -1e9;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			for (int k = 1;i+k<=n&&j+k<=n ;k++ )
			{
				int x1 = i, y1 = j;
				int x2 = i + k, y2 = j + k;
				if (Matrix[x2][y2] - Matrix[x1-1][y2] - Matrix[x2][y1-1] + Matrix[x1-1][y1-1] == 0)
				{
					ans[i][j] = k + 1;
					maxx = max(maxx, ans[i][j]);
				}
				else
					break;
			}
		}
		
	}
	printf("%d", maxx);
	return 0;
}

607 高利贷

主要是用到了快速幂和二分法

还有一点数学基础 找到判断p的表达式

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/708
//题目给定的数据显然需要用到二分0-5这个p区间
//又由于还款月最大值为300 如果用迭代一次一次计算可能会超时 而二分中
//p的值又在不断更新,通过打表看起来也是非常复杂
//所以我们使用快速幂的方式 这里依然使用二进制优化的快速幂
double n, m, k;
double qpow(double base, int pow)
{
	double result = 1;
	while (pow > 0)
	{
		if (pow & 1)
			result *= base;
		pow >>= 1;
		base *= base;
	}
	return result;
}

int check(double p)
{
	double tmp;
	tmp = (m / n - p) * qpow(1 + p, k) - (m / n);
	if (tmp < 0)return 1;
	else if (tmp > 0)return 0;
	else return 2;
}
int main()
{
	cin >> n >> m >> k;
	double l = 0, r = 5,ans=0,mid=0;
	while (l < r)
	{
		mid = (l + r) / 2;
		if (fabs(r-l) <= 1e-6)
		{
			ans = l;
			break;
		}
		if (check(mid) == 1)
		{
			r = mid;
		}
		else if (check(mid) == 0)
		{
			l = mid;
		}
		else
		{
			ans = mid;
			break;
		}
	}
	cout << fixed << setprecision(6) << ans << endl;
	return 0;
}

701 背包

此题的细节还是很多的 首先 使用基础的向上取整要考虑会不会爆

在最开始w开int时 似乎已经爆了数据 应该用long long 这里为了保险我用了unsigned long long

之后是对背包状态的模拟

如果有一件物品已经超过背包的一半而不达背包的最大值 那么直接输出YES

而对于其他情况 即物品低于背包的一半 我们直接将物品全部放入背包 只统计sum即可

如果sum可以超过背包的一半 那么最后的答案一定是YES

因为物品是否放入背包是可以有选择的 如果全部低于背包一半的物品总质量超过背包一半 那我们总可以选取正确的物品组合以超过背包一半且不超过背包

同时猜测可能会出现一个物品装不到一半,两个物品超过了背包的全部

仔细想一下 sum中存储的物品全都是不及背包一半的 如果当前背包存入的重量不达背包一半,那么再装入一个物品也不会超过背包 当且仅当已经存入的重量超过背包一半时,再装入一个物品才有可能爆掉背包 所以这种情况也不用担心

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/710
//这道题并非我们所见的背包问题
//而是一道类似模拟的题目
int t;
unsigned long long n, w;

int main()
{
	scanf("%d", &t);
	while (t--)
	{
		cin >> n >> w;
		vector<int>v(n);
		long long sum=0;
		for (int i = 0; i < n; i++)
			scanf("%d", &v[i]);
		bool flag = true;
		sort(v.begin(), v.end());
		for (auto i = v.rbegin(); i != v.rend(); i++)
		{
			if (*i >= (w - 1) / 2 + 1 && *i <= w)
			{
				printf("YES\n");
				flag = false;
				break;
			}
			else if (*i < (w - 1) / 2 + 1)
			{
				sum += *i;
			}
		}
		if (flag)
		{
			if (sum >= (w - 1) / 2 + 1)
				printf("YES\n");
			else
				printf("NO\n");
		}
	}
	return 0;
}

702 三回文序列

#include<bits/stdc++.h>
using namespace std;
int n;
int solve(vector<int>vis, vector<int>num, int i)
{
	int ans, cnt;
	ans = vis[i];
	cnt = 0;
	int l = 1, r = n;
	while (l < r)
	{
		while (l < r && num[l] != i)
		{
			vis[num[l]]--;
			l++;
		}
		while (l < r && num[r] != i)
		{
			vis[num[r]]--;
			r--;
		}
		cnt += min(vis[i], 2);
		vis[i] -= 2;
		l++, r--;
		for (int j = 1; j <= 26; j++)
		{
			ans = max(ans, cnt + vis[j]);
		}
	}
	return ans;
}
int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		cin >> n;
		vector<int>vis(30), num(n + 5);
		for (int i = 1; i <= n; i++)
		{
			cin >> num[i];
			vis[num[i]]++;
		}
		int ans = 0;
		for (int i = 1; i <= 26; i++)
		{
			if (vis[i] != 0)
			{
				ans = max(ans, solve(vis, num, i));
				
			}

		}
		cout << ans << endl;
	}
	return 0;
}

703 简单的异或问题

这道题类似于之前做过的欧拉路/一笔画问题 是纯思路上的问题 想不出来就没法做

所以我不会

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
//原题链接:http://oj.daimayuan.top/course/11/problem/732
//这道题似乎并没有题目所描述的那么简单
//需要找到正确的思路才行 这里主要是考察了异或运算的一些性质
ll n, m;
ll qpow(ll base, ll pow)
{
	ll result = 1;
	while (pow > 0)
	{
		if (pow & 1)
			result *= base;
		pow >>= 1;
		base *= base;
	}
	return result;
}
int main()
{
	cin >> n>>m;
	if (n == 0 && m != 1)
	{
		cout << qpow(2, m);
	}
	else if (m == 1)
	{
		if (n == 0)
			printf("1");
		else
			printf("2");
	}
	else
		cout << qpow(2, m) - 1;
	return 0;
}

704 子串的循环挪动问题

个人感觉就是一个模拟题 在DE的过程中得到了如下的代码

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/734
//这道题看起来是一个比较简单的模拟题
//初读感觉需要注意的点就是让k对子串的长度取模就可以
string s = " ";
string temp;
int n;

int main()
{
	cin >> temp >> n;
	s += temp;
	while (n--)
	{
		int st, ed, k;
		scanf("%d%d%d", &st, &ed, &k);
		string a = s.substr(st, ed-st+1);//cout << a<<endl;
		k %= (ed - st + 1);
		string b = a.substr(0, a.size()-k); //cout << b << endl;
		a += b;
		a.erase(0, a.size()-b.size() - k); //cout << a << endl;
		s.replace(st, ed - st + 1, a); //cout << s<<endl;
	}
	for (int i = 1; i < s.size(); i++)
		cout << s[i];
	

	return 0;
}

只要掌握

string.substr()
string.replace()
string,erase()

这道题基本上就能模拟出来了

705 弗拉德和糖果II

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
//原题链接:http://oj.daimayuan.top/course/11/problem/677
//也是一个思想上的题目
//找到最多糖果的数目m和其他糖果的数目n
//m个糖果之间有m-1个空 如果n>m-1 那么保证了糖果数最多的糖果可以吃完
ll n;
ll Max,m;
int main()
{
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		ll temp;
		cin >> temp;
		Max = max(Max, temp);
		m += temp;
	}
	m -= Max;
	if (m >= Max - 1)
		printf("YES");
	else
		printf("NO");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值