天天快乐编程2020年OI集训队 训练11题解

上次发现大家对简单题还是掌握不牢固,这次还是一些简单题,也就是比赛中第一第二题给你们练练。大家如果不是很忙的话注意一下赛后的补题,即使你是入门级的同学,提高级的题目也是可以尝试的。

1.6019: 金币

NOIP2015 普及组T1
这个题目k仅有10,000,比较小,可以直接一重循环

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int i, n, x = 1, t = 1, sum = 0;
	scanf("%d", &n);
	for (i = 1; i <= n; i++)
	{
		sum += t;
		//到x了,下次要多一个币
		if (i == x)
		{
			t++;
			x += t;
		}
	}
	printf("%d
", sum);
	return 0;
}

模拟的方法有麻烦的,有简单的,建议写简单的方法
这个题目存在一个通项公式的写法吗,前面是在进行平方和,平方和有公式的,那成了

[1^2+2^2...+n^2=n(n+1)(2n+1)/6\ 1+2+3+...+n=n(n+1)/2 ]

如果过了以k金币为结束的n天,那么就有一条方程
n * (n+1)=2 * k。即k=(sqrt(1+8 * n)-1)/2

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	cin >> n;
	int k = (sqrt(1 + 8 * n + 0.5) - 1) / 2;
	int sum = (k * (k + 1) * (2 * k + 1)) / 6;
	//求出剩下的部分
	n -= k * (k + 1) / 2;
	sum += n * (k + 1);
	cout << sum;
	return 0;
}

2.5991: 成绩

NOIP2017 普及组T1
就这样一个简单题,当时也是有人没做对,OI赛制提交了不能实时得到结果,解题时一定要小心谨慎

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int a, b, c;
	cin >> a >> b >> c;
	printf("%.f", a * 0.2 + b * 0.3 + c * 0.5);
}

3.4843: 花生采摘

NOIP2004普及组T2
这也是个模拟题,需要注意没有两株花生株的花生数目相同
我们想要采摘到最多的花生数直接从最大的开始采摘即可,然后每次时间够就去摘,求的是曼哈顿距离

#include <bits/stdc++.h>
using namespace std;

struct T
{
	//用结构体存坐标和数量及时间。
	int x, y, time, w;
} a[405];
int cmp(T a,T b)
{
	return a.w>b.w;
}
int main()
{
	int m, n, t;
	cin >> m >> n >> t;
	//记录有花生的个数
	int tot = 0;
	for (int i = 0; i < m; i++)
		for (int j = 0, x; j < n; j++)
		{
			cin >> x;
			if (x > 0)
			{
				//下面有花生的时候就存储,记得下标从1开始
				a[tot++] = {i+1,j+1,0,x};
			}
		}
	//从大到小排序
	sort(a,a+tot,cmp);
	int ans=0;
	//枚举每个花生
	for (int i = 0; i < tot; i++)
	{		  
		if (i == 0)
		{
			//第一个花生多多一开始可以跳到第一个最多花生的所在列。
			a[i].time = a[i].x + 1;
		}
		else
		{
			//不是第一个的话就加上与前一个的坐标差再加采摘时间。
			a[i].time = a[i - 1].time + abs(a[i].x - a[i - 1].x) + abs(a[i].y - a[i - 1].y) + 1;
		}
		//如果数据合法那么就把花生数加上
		if (a[i].time + a[i].x <= t)
			ans += a[i].w;
	}
	cout << ans;
	return 0;
}

也有人深搜的,不过这个深搜和循环也基本一样啊

#include <iostream>
#include <algorithm>
using namespace std;
typedef struct p p;
struct p
{
    int x,y,num;
}s[405];
int m,n,k,index,ans;
int a[25][25];
bool cmp(p a,p b)
{
    return a.num>b.num;
}
void dfs(int x)
{
    int tmp,dis;
    if(x>=index)
        return;
    if(x==0)
        dis=s[0].x;
    else
        dis=abs(s[x-1].x-s[x].x)+abs(s[x-1].y-s[x].y);
    tmp=k-dis-1;
    if(tmp>=s[x].x)///能回去
        ans+=s[x].num,k=k-dis-1,dfs(x+1);
    return;
}
int main()
{
    cin>>m>>n>>k;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            cin>>a[i][j];
            if(a[i][j])
                s[index].x=i,s[index].y=j,s[index++].num=a[i][j];
        }
    sort(s,s+index,cmp);
    dfs(0);
    cout<<ans<<'
';
    return 0;
}

4.4869: 寻宝

NOIP2012普及组T2
真正的模拟题
指示牌上有一个数字x,表示从这个房间开始按逆时针方向选择第x个有楼梯的房间,x的大小是1,000,000,如果直接上肯定是超时的。有没有方法能够不超时呢,我们可以使用取余,也就是考虑下周期性问题
%10会得到[0,10),对该层楼梯门个数进行取余,会出现0导致我们现在的门又没楼梯。为了避免这种情况,房间编号我们可以从1开始,0用来存储本层楼梯通往上一层的楼梯个数。取余个数然后加上a[i]0,这样就没有问题了

#include <bits/stdc++.h>
using namespace std;
//数组不要开小了,我开小get WA一次
const int N = 10005, M = 105;
int n, m, a[N][M], num[N][M];
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			scanf("%d%d", &a[i][j], &num[i][j]);
			//a[i][0]储存本层有多少楼梯通往上一层
			if (a[i][j])
				a[i][0]++;
		}
	int t, ans = 0;
	cin >> t;
	//房间编号往后挪了1
	t++;
	for (int i = 1; i <= n; i++)
	{
		ans = (ans + num[i][t]) % 20123;
		//+a[i][0]是为了防止模了之后为0(包含本层只有一个楼梯)
		int x = num[i][t] % a[i][0] + a[i][0];
		t--;
		while (x)
		{
			t++;
			if (t > m)
				t = 1;
			if (a[i][t])
				x--;
		}
	}
	cout << ans;
	return 0;
}

5.4876: 转圈游戏

NOIP2013提高组Day1T1
也就是1次你走到(x+m)%n的位置,10 ^ k次为(x+ m * 10 ^ k)%n的位置
满足随时取余定理,可以直接用快速幂进行求解

#include <bits/stdc++.h>
using namespace std;
int ksm(int a, int b, int Mod)
{
	int ans = 1, t = a;
	while (b)
	{
		//2进制位是1,就乘上,&按位与运算
		if (b & 1)
			ans = ans * 1LL * t % Mod;
		t = t * 1LL * t % Mod;
		//每次都/2,和右移(>>)一位相同
		b >>= 1;
	}
	return ans;
}
int main()
{
	int n, m, k, x;
	cin >> n >> m >> k >> x;
	cout << (x % n + m % n * ksm(10, k, n) % n) % n;
	return 0;
}

6.4879: 积木大赛

NOIP2013提高组Day2T1
不用线段树,也不用递归,可以直接模拟
我们可以把序列分成(a1,..ai)(ai+1,...aj)......(ak,...an)多个非递减序列。
然后所有段中最大值的和减去除第一段外的段的最小值,那么直接记录两个数字的差是否可以呢
设q为左边一堆高度,p为右边一堆高度,s为总摆放次数。
1.q<p,即左边的一堆比右边矮,左边的一堆摆完后,右边的还差一点,那么摆放次数s加上两堆的高度差p-q(相当于摆好了右堆)。
2.q>=p,即左边的一堆比右边矮,说明只要左边的一堆堆好了,那么右边的一堆也肯定堆好了,所以不需要增加摆放次数s。
所以猜测是正确的

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int n, last = 0, ans = 0;
	cin >> n;
	for (int i = 0,x; i < n; i++)
	{
		cin >> x;
		//左边低,需要加上
		if (x > last)
			ans += (x - last);
		last = x;
	}
	cout << ans << endl;
}

7.4886: 生活大爆炸版石头剪刀布

NOIP2014提高组Day1T1
直接写出这个输赢表即可,一个一个写if累死了,同理的还有TZOJ5137: 骰子

#include <bits/stdc++.h>
using namespace std;
//直接写出这个输赢表
int ce[5][5] = {
	0, -1, 1, 1, -1,
	1, 0, -1, 1, -1,
	-1, 1, 0, -1, 1,
	-1, -1, 1, 0, 1,
	1, 1, -1, -1, 0};
int a[205], b[205];
int main()
{

	int n, na, nb;
	cin>>n>>na>>nb;
	for (int i = 0; i < na; i++)
		cin >> a[i];
	for (int i = 0; i < nb; i++)
		cin >> b[i];
	int apos = 0, bpos = 0, awin = 0, bwin = 0;
	for (int i = 1; i <= n; i++)
	{
		if (ce[a[apos]][b[bpos]] == 1)
			awin++;
		if (ce[a[apos]][b[bpos]] == -1)
			bwin++;
		apos++;
		bpos++;
		//到尾从头开始
		if (apos == na)
			apos = 0;
		if (bpos == nb)
			bpos = 0;
	}
	cout<<awin<<" "<<bwin;
	return 0;
}

原文地址:https://www.cnblogs.com/BobHuang/p/13898850.html