第十七周 DMY 每日一题

目录

T1 碰撞2

思路:

代码:

T2 优美!最长上升子序列

思路:

代码:

T3 巨大的牛棚

思路:

代码:

T4 高利贷

​编辑思路:

代码:

T5 背包

 思路:

代码:

T6 三回文序列

思路:

代码:

T7 简单的异或问题

 思路:

代码:

 T8 字串的循环挪动

思路:

代码:

T9 弗拉德和糖果

​编辑思路:

代码:

T10 上帝的集合 

​编辑思路:

代码:


T1 碰撞2

思路:

只要按照判断同一行中按照列从大到小排序后是否存在列数小的往右走列数小的往左边走,如果有则会发生碰撞

代码:


#include<bits/stdc++.h>
using namespace std;
struct W
{
	int x;
	int y;
	char to;
};
W a[200005];
int main()
{
	int n; cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i].x >> a[i].y;
	}
	string s; cin >> s;
	s = '0' + s;
	for (int i = 1; i <= n; i++)	
	{
		a[i].to = s[i];
	}

	sort(a + 1, a + 1 + n, [](W a, W b)
		{
			return a.y < b.y || (a.y == b.y && a.x < b.x);
		});
	for (int i = 1; i < n; i++)
	{
		if (a[i].y == a[i + 1].y)
		{
			if (a[i].to == 'R' && a[i+1].to == 'L')
			{
				cout << "Yes" << endl;
				return 0;
			}
		}
	}
	cout << "No" << endl;
	return 0;
}

T2 优美!最长上升子序列

思路:

类似于传统的解决最长上升子序列,利用dp来求解,只不过这里有个特殊条件,我们只需要从前往后,将目前的标号作为因子往后去更新其倍数就可以了

代码:

#include<bits/stdc++.h>
using namespace std;
int a[1000005],dp[1000005];
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int n, maxn =1;
		cin >> n;
		for (int i = 0; i <= n; i++)dp[i] = 1;
		for (int i = 1; i <= n; i++)cin >> a[i];
		for (int i = 1; i <= n; i++) {
			for (int j = 2 * i; j <= n; j += i) {
				if (a[i] < a[j])dp[j] = max(dp[j], dp[i] + 1);
				maxn = max(maxn, dp[j]);
			}
		}
		cout <<maxn<< endl;
	}

}

T3 巨大的牛棚

思路:

采用二维前缀和和二分答案,注意预处理为mp[x][y]=mp[x-1][y]+mp[x][y-1]-mp[x-1][y-1]+a[x][y]

其中a[x][y]用来储存树的位置,假设有树则a[x][y]=1

因为随着变长变大原本没有树的方块会变得有树,满足单调性,故可以采用二分答案确定边长,

然后再逐个判断是否满足即可,具体实现看代码。

代码:

#include<bits/stdc++.h>
using namespace std;
int mp[1005][1005],f[1005][1005];
int n;
bool check(int x)
{
	for (int i = 1; i <= n-x+1; i++)
	{
		for (int j = 1; j <= n-x+1; j++)
		{
			int x1 = i, y1 = j, x2 = i+ x-1, y2 = j + x-1;
			if (f[x2][y2] - f[x1 - 1][y2] - f[x2][y1 - 1] + f[x1 - 1][y1 - 1] == 0)
			{
				return true;
			}
		}
	}
	return false;
}
int main()
{
	 cin >> n;
	int t; cin >> t;
	while (t--)
	{
		int x, y; cin >> x >> y;
		mp[x][y] = 1;
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			f[i][j] = f[i - 1][j] + f[i][j - 1] + mp[i][j] - f[i - 1][j - 1];
		}
	}
	int l = 1, r = n, ans = 0;
	while (l<r)
	{
		int mid = (l + r+1) / 2;
		if (check(mid))
		{
			ans = mid;
			l = mid ;
		}
		else
		{
			r = mid - 1;
		}
	}
	cout << l << endl;
	return 0;
}

T4 高利贷

思路:

一个简单的浮点数的二分答案,没有什么难度

代码:

#include<bits/stdc++.h>
using namespace std;
double n, m, k;
bool check(double x)
{
	double ans = 0;
	double p = 1 + x;
	for (int i = 1; i <= k; i++)
	{
		ans += m / p;
		p *= (1 + x);
	}
	if (ans >= n)return true;
	else
	{
		return false;
	}
}
int main()
{
	 cin >> n >> m >> k;
	double r = 5, l = 0;
	while (r-l>1e-8)
	{
		double mid = (r + l) / 2;
		if (check(mid))
		{
			l = mid;
		}
		else r = mid;
	}
	cout << fixed << setprecision(6) << l << endl;
	return 0;
}

T5 背包

 思路:

只需要在输入的时候判断是否有一个物品直接就满足在m+1/2<=x<=m的区间内,如果有的话可以记录一下后续直接输入yes即可,如果没有的话则将所有x<m+1/2的物品依此加上,同时每次都判断一下是否总和满足答案的区间,同时注意到w最大可以达到1e18则要用long long

代码:

#include<bits/stdc++.h>
using namespace std;
long long a[200005];
int main()
{
	int t; cin >> t;
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	while (t--)
	{
		long long  n, m;
		cin >> n >> m;
		bool flag = 0;
		for (int i = 1; i <= n; i++)
		{
			cin >> a[i];
			if (a[i] >= ((m+1)/ 2) && a[i] <= m	)
			{
				flag = true;
			}
		}
		if (!flag)
		{
			long long  ans = 0;
			for (int i = 1; i <= n; i++)
			{
				if (a[i] < (m + 1) / 2)
				{
					ans += a[i];
					
				}
				if (ans >= (m + 1) / 2 && ans <= m)
				{
					flag = true;
					break;
				}
				if (ans > m)
				{
					break;
				}
			}
		}
		if (flag)
		{
			cout << "YES" << endl;
		}
		else
		{
			cout << "NO" << endl;
		}
	}
	return 0;
}

T6 三回文序列

思路:

利用二维前缀和和双指针,首先进行预处理,用sum[x][y]来记录第一维来记录每个数字,由题目可知1<=x<=26,第二维则用来储存位置,整个表示为在前j个数字中有sum[i][j]个i,利用首先遍历26个数字和双指针来确定两边的数字和最大数字,再遍历中间数字和利用前缀和来确定中间最大个数,最后两边数字最大个数之和加上中间数字的最大个数就是答案。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 5;
int n, a[N], sum[30][N];
inline void solve() {
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];
	for (int i = 1; i <= 26; i++)
		for (int j = 1; j <= n; j++) 
			sum[i][j] = sum[i][j - 1] + (a[j] == i);
	int ans = 0; //记录答案
	for (int c1 = 1; c1 <= 26; c1++) {
		int l = 0, r = n + 1, nl = 0, nr = 0; //左右指针
		for (int cnt = 1; cnt <= n; cnt++)
		{
			while (nl < cnt && l < r) l++, nl += (a[l] == c1); //移动指针,维护数量
			while (nr < cnt && l < r) r--, nr += (a[r] == c1);
			if (nl != cnt || nr != cnt) break; //无法满足,结束
			for (int c2 = 1; c2 <= 26; c2++) {
				if (c1 == c2)continue;
				if (l < r) ans = max(ans, cnt * 2 + sum[c2][r - 1] - sum[c2][l]);
				else ans = max(ans, cnt * 2 - 1);
			}
		}
	}
	cout << ans << '\n';
}
signed main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int T; cin >> T; while (T--) solve();
}

T7 简单的异或问题

 思路:

可以通过异或的性质可以知道当两个数相加的为2^m-1的时候则俩个数异或之后的结果全是1,同时很容易知道对于除了当m=1的情况以外其他都是有偶数对相加等于2^m-1的存在,故选所有的进行异或后得到的结果为0,而当取奇数对进行异或后会得到m个1,则假设对剩下的x进行异或将会得到2^m-1-x,故对于样例中要得到3,除了3不取之外其他的都取即可,但是对于m=1的情况下只有1对,则需要进行特判

代码:

#include<bits/stdc++.h>
using namespace std;
long long qiumi(int x)
{
	long long ans = 1;
	long long y = 2;
	while (x)
	{
		if (x & 1)
		{
			ans *= y;
		}
		x >>= 1;
		y *= y;
	}
	return ans;
}
int main()
{
	long long  n, m; cin >> n >> m;
	if (m == 1)
	{
		if (n == 0)
		{
			cout << "1" << endl;
		}
		else
		{
			cout<<"2"<<endl;
		}
	}
	else
	{
		long long ans = qiumi(m);
		if (n == 0)
		{
			cout << ans << endl;
		}
		else
		{
			cout << ans - 1 << endl;
		}
	}
	return 0;
}

 T8 字串的循环挪动

思路:

主要是利用string中的substr函数,不知道的可以自行在csdn上进行查找一下,主要将原本的S进行分成三段,主要难点是对中间那一段的处理,对于挪动问题我们可以先将需要处理的字符串进行倍增处理,然后对移动次数取模原本字符串的长度,毕竟假设和原本字符串长度相同则会变成原本的长度,然后再从r-l+1-k开始取,取长度为r-l+1的字符串就是循环移动后的结果,就拿abacaba为例子,中间那一段为acab,加倍后为acabacab,从6-3+1-1=3开始(注意字符串默认从0开始),然后往后面取4个则得到baca。

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	string s;
	cin >> s;
	s = '0' + s;
	int n; cin >> n;
	while (n--)
	{
		int l, r, t;
	cin >> l >> r >> t;
	t = t % (r - l + 1);
	string a, b, c;
	a = s.substr(1, l-1);
	c = s.substr(r+1, s.size() - r-1);
	b = s.substr(l, r - l+1);
	/*cout << b << endl;*/
	b += b;
	/*cout << b << endl;*/
	b = b.substr(r-l+1-t, r - l+1);
	s = '0' + a + b + c;
	/*cout << a << endl << b << endl << c << endl;
	cout << s << endl;*/
	}
	s = s.substr(1, s.size() - 1);
	cout << s << endl;
	return 0;
}

T9 弗拉德和糖果

思路:

一道简单的数学问题,假如最大值大于除了最大值之外的所有值的总和值是肯定会吃到连续的糖果

而当最大值小于等于除了最大值之外的所有值的总和值则是可以的,但是需要特判一下只有一种糖果的情况,如果那种糖果只有一个则是可以的但是大于1的话则不可以。

代码:

	#include<bits/stdc++.h>
	using namespace std;
	int main()
	{
		ios_base::sync_with_stdio(false);
		cin.tie(nullptr);
		cout.tie(nullptr);
		int n; cin >> n;
		long long maxn = 0, cnt = 0;
		bool flag = 0;
		if (n == 1)
		{
			flag = 1;
		}
		while (n--)
		{
			long long x;
			cin >> x;
			cnt += x;
			maxn = max(x, maxn);
		}
		long long excpt = cnt - maxn;
		if (flag)
		{
			if(maxn==1)
			cout << "YES" << endl;
			else
			{
				cout << "NO" << endl;
			}
			return 0;
		}
		if (maxn <= excpt)
		{
			cout << "YES" << endl;
		}
		else
		{
			cout << "NO" << endl;
		}
		return 0;
	}

T10 上帝的集合 

思路:

主要是对操作2的处理,我们可以利用一个cnt 来记录之前所加上的x的总和,然后再对添加的数采取储存这个数减去cnt的值,最后输出的时候再输出那个数加上cnt就好了,可以利用priority_queue来简化时间复杂度

代码:

#include<bits/stdc++.h>
using namespace std;
priority_queue<long long,vector<long long >,greater<long long> >V;
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int n; cin >> n;
	long long cnt = 0;
	while (n--)
	{
		int op; cin >> op;
		long long x;
		if (op == 1)
		{
			cin >> x;
			V.push(x-cnt);
		}
		else if (op==2)
		{
			cin >> x;
			cnt += x;
		}
		else
		{
			cout << V.top()+cnt <<'\n';
			V.pop();
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值