week4

这篇文章包含一系列编程题目,涉及数组的特殊操作,寻找特定数对,数位的累加计算,解决国王游戏中的权力分配问题,完美数的查找,Lusir游戏中能量选择的策略以及使用BFS解决树形结构问题和01序列的计数。每道题都提供了C++代码实现。
摘要由CSDN通过智能技术生成

第一题 数组操作

题面http://oj.daimayuan.top/course/11/problem/610
出题人的表达能力有待加强,大意就是把后面一个区间中的所有数赋给前面的一个区间,所以只能把所有数变成最后一个数,因为只能后赋前,所以我们就从后往前遍历,相同跳过,不同num++,并且i-=n-2-i,写的时候被我for里面的i–给坑了。。。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 10;
int ori[maxn];
int main() {
	int t;
	scanf("%d", &t);
	while (t--)
	{
		int n;
		scanf("%d", &n);
		for (int i = 0; i < n; i++) {
			scanf("%d", &ori[i]);
		}
		int num = 0;
		int last = ori[n - 1];
		int i;
		for (i = n - 2; i >= 0; i--) {
			if (ori[i] == last)continue;
			num++;
			i -= n -2 - i;
		}
		//if (i != -1) {
		//	num++;
		//}
		printf("%d\n", num);
	}
}

第二题 A-B 数对

题面http://oj.daimayuan.top/course/11/problem/616
用map存每个数字出现的次数,之后num+=当前数字出现的个数*当前数字+c出现的个数。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 10;
map<int, int> num_shuzi;
int main() {
	int n,c;
	scanf("%d%d", &n,&c);
	for (int i = 0; i < n; i++) {
		int temp;
		scanf("%d", &temp);
		num_shuzi[temp]++;
	}
	map<int, int>::iterator it;
	int num = 0;
	for (it = num_shuzi.begin(); it != num_shuzi.end(); it++) {
		if (it->second == 0)continue;
		num += it->second * num_shuzi[it->first + c];
	}
	printf("%d", num);
}

第三题 数位计算

题面http://oj.daimayuan.top/course/11/problem/666
大概,就是求和,10到99,100到999这样,但1到9要单独讨论,

#define _CRT_SECURE_NO_WARNINGS
#define ll long long
#include<bits/stdc++.h>
using namespace std;
const int mod = 998244353;
ll n;
int main() {
	scanf("%lld", &n);
	ll temp = n;
	int num = 0;
	ll sum = 0;
	ll s0 = 1, s9 = 9;
	while (temp > 9)
	{
		num++;
		temp /= 10;
		if (num == 1) {
			sum += (s9 - s0 + 1) * (1 + 9) / 2;
		}
		else {
			sum += ((s9 - s0 + 1) * (2 + s9 - s0) / 2)% mod;
		}
		//sum += (s9 - s0) + 1;
		s9 = (s9 * 10 + 9)%mod;
		s0 *= 10;
		s0% mod;
	}
	sum += ((n - s0 + 1) * (1 + (num == 0 ? n : n - s0 + 1)) / 2)% mod;
	printf("%lld", sum%mod);
}

第四题 新国王游戏

题面:http://oj.daimayuan.top/course/11/problem/672
最开始的想法是,左手上的所有数都要累乘,之后再分别乘右手的数,如此,可知,把左手越大的数越往后面放,可以让这个大数对和的贡献更大。但发现一个反例,
2
8 1
10 100
明显
10 100
8 1
更大
根据这个特例,猜测cmp函数应改成
bool cmp(int a, int b) {
return !(ori[a][1]*ori[b][0]+ori[b][1]>ori[b][1]*ori[a][0]+ori[a][1]);
}
后续查到这个方法叫交换法?大致原理就是
ai代表第i个人
a1,a2,a3,ai,ai+1,an
对于ai和ai+1,若把ai+1与ai交换,前面的大臣的获得不会变,后面的也不会变,因为只是乘,所以对于所有的ai+1和ai,若ai+1放前面答案会更大,就要交换,从整体来看,所有满足交换后更大的两项都要交换,类似于冒泡排序,既然是排序,可以直接用sort。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
const int maxn = 1e6 + 10;
const int MOD = 1000000007;
long long ori[maxn][2];
int orinew[maxn];
//记录顺序下标
bool cmp(int a, int b) {	
	return ori[a][1]*ori[b][0]+ori[b][1]>ori[b][1]*ori[a][0]+ori[a][1];
}
int main() {
	//ios::sync_with_stdio(false);
	//cin.tie(0);
	//cout.tie(0);
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		scanf("%d%d", &ori[i][0], &ori[i][1]);
		orinew[i] = i;
	}
	sort(orinew, orinew + n, cmp);
	long long sum = 0;
	long long suml = 1;
	for (int i = n-1; i >= 0; i--) {
		sum = (sum+suml * ori[orinew[i]][1])%MOD;
		suml = (suml*ori[orinew[i]][0])%MOD;
		
	}
	//sum = (sum+ori[orinew[n - 1]][1])%MOD;
	printf("%lld",sum);
	// ll sum = 1;
    // ll res1 = ori[orinew[0]][1];
	// for (int i = 0; i < n; i++)
	// {
	// 	if (i > 0)res1 = (res1 + (sum * ori[orinew[i]][1]) % MOD) % MOD;
	// 	sum = (sum * ori[orinew[i]][0]) % MOD;
	// }	
	// printf("%lld",res1);
    // return 0;
}

第五题 完美数

题面:http://oj.daimayuan.top/course/11/problem/669
先找出几个a,几个b的情况符合题意,再组合数插空算num,但是,这个鬼取模不能用先乘后除的方法取模算,只能用c m/n=c m/n-1+c m-1/n-1,递归取模来算,

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#define ll long long
const int mod = 1e9 + 7;
const int maxn = 1e6 + 10;
using namespace std;
ll a, b, m;
//ll zuhe(ll i) {
//	ll sum = 1, up = min(i, m - i), down = m - up+1;
//	for (ll j = 1; j <= up;j++) {
//		sum = (sum * (down-up+j)/j) % mod;
//	}
//	//for (ll j = up; j >= 1; j--) {
//	//	sum /= j;
//	//}
//	return sum;
//}
map<ll, map<ll, ll>>res;
ll zuhe(ll down,ll up) {
	if (up == 0 || up == down) return 1;
	if (res[down][up] != 0)return res[down][up];
	return res[down][up] = (zuhe(down - 1, up) + zuhe(down, up - 1))%mod;
}

int main() {
	scanf("%lld%lld%lld", &a, &b, &m);
	if (a > b)swap(a, b);
	ll min1 = a * m;
	ll num = 0;
	ll temp = min1;
	while (temp > 0)
	{
		if (temp % 10 != a && temp % 10 != b) {
			num--;
			break;
		}
		temp /= 10;
	}
	num++;
	//全为a
	for (ll i = 1; i <= m-1; i++) {
		//有i个b,
		min1 = min1 - a + b;
		temp = min1;
		bool no = 0;
		while (temp>0)
		{
			if (temp % 10 != a && temp % 10 != b) {
				no=1;
				break;
			}
			temp /= 10;
		}
		if (!no) {
			ll up = min(i, m - i);
			num = (num+zuhe(m-up+1,up))%mod;
		}
	}
	min1 = b * m;
	temp = min1;
	while (temp > 0)
	{
		if (temp % 10 != a && temp % 10 != b) {
			num--;
			break;
		}
		temp /= 10;
	}
	num++;
	printf("%lld", num);
}

第六题 Lusir的游戏

题面http://oj.daimayuan.top/course/11/problem/674
初始能量取值范围在[最小高度,最大高度]
在模拟一个能量能不能选的时候,若半途这个能量已近累加到比最大还大了,就不用算后面的了,一定可以。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
long long ori[maxn];
int n;
int main() {
	scanf("%d", &n);
	long long minori = 2e5, maxori = -2;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &ori[i]);
		minori = min(minori, ori[i]);
		maxori = max(maxori, ori[i]);
	}
	for (int i = minori; i <= maxori; i++) {
		long long temp = i;
		bool yes = 1;
		for (int j = 1; j < n; j++) {
			temp += temp - ori[j];
			if (temp > maxori) {
				break;
			}
			if (temp < 0) {
				yes = 0;
				break;
			}
		}
		if (yes) {
			cout << i;
			return 0;
		}
	}
	cout << maxori;
}

第七题 BFS练习

题面http://oj.daimayuan.top/course/11/problem/147
题读错了我去,这题应该是在由a构成的树里面找bi,而我傻啦吧唧的写成了每次都从头开始找bi,应该是一次找完所有bi,每个bi的步骤等于它所在的deep,我还傻乎乎的写了个A*来优化。。。。。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int a, q, n;
//int ori[maxn];
//int minlen = 0x7fffffff;
struct node
{
	int num;
	int coast;
	friend bool operator <(node a, node b) {
		return a.coast > b.coast;
	}
};
map<int, int> again;
map<int, int> len;
void bfs(int n) {
	priority_queue<node> dui;
	node ori;
	ori.coast = abs(n - a);
	ori.num = a;
	dui.push(ori);
	again[a] = 1;
	//int len = 0;
	while (!dui.empty()) {
		node duitop = dui.top();
		dui.pop();
		if (duitop.num < 0)continue;
		if (duitop.num == n) {
			printf("%d ", len[n]);
			break;
		}
		//again[duitop] = 1;
		if (!again[duitop.num + 1]) {
			node temp = duitop;
			temp.num++;
			temp.coast = abs(n - temp.num);
			dui.push(temp);
			len[duitop.num + 1] = len[duitop.num] + 1;
			again[duitop.num + 1] = 1;
		}
		if (!again[duitop.num - 1]) {
			node temp = duitop;
			temp.num--;
			temp.coast = abs(n - temp.num);
			dui.push(temp);
			len[duitop.num - 1] = len[duitop.num] + 1;
			again[duitop.num - 1] = 1;
		}
		if (!again[duitop.num * 2]) {
			node temp = duitop;
			temp.num*=2;
			temp.coast = abs(n - temp.num);
			dui.push(temp);
			len[duitop.num * 2] = len[duitop.num] + 1;
			again[duitop.num * 2] = 1;
		}
		if (!again[duitop.num * 3]) {
			node temp = duitop;
			temp.num*=3;
			temp.coast = abs(n - temp.num);
			dui.push(temp);
			len[duitop.num * 3] = len[duitop.num] + 1;
			again[duitop.num * 3] = 1;
		}
	}
}
int main() {
	scanf("%d%d", &a, &q);
	while (q--)
	{
		scanf("%d", &n);
		again.clear();
		len.clear();
		//minlen = 0x7fffffff;
		bfs(n);
		//cout << minlen<<" ";
	}
}

第八题 01序列2

题面http://oj.daimayuan.top/course/11/problem/700
看取模猜是dp递推出来的,然后确实
dp[i]代表长为i,k个0的种类
dp[i-1]中的所有类型,后面在填1或0,得到dp[i-1]*2种,这之中加0的肯定可行,而另一半要减,末尾是1的,10,100的,具体0取决于k,一共要减k次,每次减dp[i-k-2-j]个

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
ll dp[maxn];

int main() {
	dp[0] = 1;
	ll n, k;
	scanf("%lld%lld", &n, &k);
	for (int i = 1; i <= k; i++) {
		dp[i] = i + 1;
	}
	for (ll i = k+1; i <= n; i++) {
		ll temp = dp[i-1];
		for (int j = 0; j < k; j++) {
			temp = (temp-dp[i - k - 2 - j>0?i-k-2-j:0]);
			if (temp < 0)cout << temp << endl;
		}
		dp[i] = ( temp+ dp[i - 1])%mod;
	}
	cout << dp[n]%mod;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值