天天快乐编程2020年OI集训队 训练10题解
本次训练题目类型为模拟。在我们的比赛中,往往会存在这样的题目,题目不难,但是比较繁琐,如果要写对需要我们先花时间去题目进行分析,然后思考一些小的点,然后再动手去敲代码,这种题被称为模拟题,也有人叫他暴力,暴力出奇迹。
1.4882: 珠心算测验
NOIP2014普及组T1
题意也就是统计n个数两两相加后在数列里存在。
由于数字比较小,我们可以枚举所有的结果,让后把它放进桶里。
#include <bits/stdc++.h>
using namespace std;
//20005就够用,最大值是10000+10000=20000
const int N = 20005;
//t是桶,M1[i]表示值为i的数在集合中两两相加出现了几次,M2[i]表示值为i的数是否在原集合中
int M1[N], M2[N];
int n, a[105], ans, maxn;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
M2[a[i]] = 1;
}
//暴力枚举求出可以加出的数
for (int i = 1; i < n; i++)
{
for (int j = i + 1; j <= n; j++)
{
M1[a[i] + a[j]]++;
//求求出数中最大值
maxn = max(maxn, a[i] + a[j]);
}
}
//只需要枚举到最大值即可
for (int i = 1; i <= maxn; i++)
{
//判断是否满足,满足就ans++
if (M1[i] > 0 && M2[i])
ans++;
}
cout << ans;
return 0;
}
candy0014的暴力寻找
#include <bits/stdc++.h>
using namespace std;
int n,a[105],ans;
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
for(int k=2;k<n;k++)
for(int i=1;i<k;i++)
for(int j=0;j<i;j++)
if(a[k]==a[i]+a[j])
{
ans++,i=k;
break;
}
cout<<ans<<"\n";
return 0;
}
直接用STL的set也是很舒服的
#include <bits/stdc++.h>
using namespace std;
//查找集合
set<int> num;
//去重集合
set<int> res;
int n, a[105], tmp;
int main()
{
cin >> n;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
//用集合方便查找
num.insert(a[i]);
}
for (int i = 1; i <= n; ++i)
{
for (int j = i + 1; j <= n; ++j)
{
//求出所枚举的两数之和
int tmp = a[i] + a[j];
//直接在集合中查找tmp
if (num.count(tmp))
{
//直接插入,会自动去重
res.insert(tmp);
}
}
}
cout << res.size(); //直接输出集合内元素数量
return 0;
}
2.4873: 表达式求值
NOIP2013普及组T2
给你一个只包含加减的式子,让你求值。
题目说当答案长度多于4位时,请只输出最后4位,前导0不输出。为什么要有这个条件呢,因为想让你用随时取余定理,加乘是可以随时取余的
我们自己会怎么计算呢,先把乘法算法,再计算加减,所以就可以用栈的思想去做,但是处理的过程中我们要去解决这些符号
#include <bits/stdc++.h>
using namespace std;
//一个存数字并在最后把它们相加的栈
stack<int> S;
int main()
{
int a, b;
char c;
//先输入一个数,以后符号+数字输入,假设前面是0+
cin >> a;
//压入栈中
S.push(a % 10000);
while (cin >> c >> b)
{
//若是乘法,将*之前的数字与*之后的数字积存入
if (c == '*')
{
int x = S.top();
S.pop();
//计算乘积,并压入栈
S.push(x * (b % 10000) % 10000);
}
else
{
S.push(b % 10000);
}
}
int ans = 0;
while (!S.empty())
{
ans = (ans + S.top()) % 10000;
S.pop();
}
cout << ans;
return 0;
}
直接利用字符串进行模拟也是可以的
#include<stdio.h>
#include<string.h>
int main()
{
char a[1000000];
__int64 i,f=0,x,b[100010]={0},l=0,r,n;
gets(a);
n=strlen(a);
for(i=0;i<n;i++)
{
while(a[i]>='0'&&a[i]<='9'&&i<n)
{
b[l]=b[l]*10+a[i]-'0';
i++;
}
if(f)
b[l]=b[l]*x%10000;
if(a[i]=='+')
{l++;f=0;}
else if(a[i]=='*')
{
x=b[l];b[l]=0;f=1;
}
}
r=0;
for(i=0;i<=l;i++)
r=r+b[i]%10000;
printf("%I64d\n",r%10000);
}
3.6276: 公交换乘
CSP2019入门级T2
无情模拟,用一个数组来装所有的收集到的赠票。每当坐地铁的时候,就直接花钱,然后获得一张赠票,放到数组里面。每当坐公交的时候,看看数组里面有没有时间合适,价格小于现在公交票价的赠票,并且没用过的赠票,直接用时间最早的那一张就可以了。而且45分钟后就过期了,可以用一个队列记录下这张票,这个题卡常,可以直接用数组模拟队列
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
struct Ticket
{
//赠票的价格,最晚使用时间和是否需用过
int price, time, used;
} q[MAXN]; //赠票盒子
int head, tail, n, cost;
int main()
{
cin >> n;
for (int i = 0; i < n; ++i)
{
int op, price, time;
//输入每次坐车的种类,价格和发车时间
cin >> op >> price >> time;
if (op == 0)
{
//如果是坐地铁,直接把价格加到cost里面
cost += price;
//新一张赠票插入数组末尾,这张票的最晚使用时间是当前时间+45
q[tail].time = time + 45;
//赠票面额就是地铁票价
q[tail++].price = price;
}
else
{
//先用一个循环把过期票扔掉
while (head < tail && q[head].time < time)
{
head++;
}
bool found = false; //表示是否有合适的赠票,先假设没有
for (int j = head; j < tail; ++j)
{
//循环所有剩余的票,这些一定都没过期,因为题目中时间是按顺序给我们的
if (q[j].price >= price && q[j].used == 0)
{
//如果价格合适,并且没用过,标记找到了,这张票标记用过
found = true;
q[j].used = 1;
break;
}
}
//如果没找到合适的赠票,花钱即可
if (!found)
cost += price;
}
}
cout << cost;
return 0;
}
4.4884: 螺旋矩阵
NOIP2014普及组T3
这个题我们可以想办法去拿那50%的分数,也就是暴力模拟
设置上下左右四个变量,然后暴力每次填数
int j = 1, now = 0, U = 1, D = m, L = 1, R = n;
i = 1;
while (now < N)
{
while (j < R && now < N)
{
c[i][j] = a[now++];
j++;
}
while (i < D && now < N)
{
c[i][j] = a[now++];
i++;
}
while (j > L && now < N)
{
c[i][j] = a[now++];
j--;
}
while (i > U && now < N)
{
c[i][j] = a[now++];
i--;
}
i++;
j++;
U++, D--;
L++, R--;
//最后一个是特殊的
if (now == N - 1)
c[i][j] = a[now++];
}
我们也可以把上面的方法,再用递归实现下,也就是只记录填到哪个数字了,不用数组,一层填4 * (n-1),每次少两行,不断递归下去
1.如果是第 1 行,那么第 j 列的数字就是 j;
2.如果是第 n 列,那么第 i 行的数字就是 n + i - 1;
后两条规律有点难找,但是不要放弃,继续观察:
3.如果是第 n 行,那么第 j 列的数字就是 3×n−2−j+1;
4.如果是第 1 列,那么第 i 行的数字就是 4×n−4−i+2。
int work(int n, int i, int j) {
if (i == 1)
return j;
if (j == n)
return n + i - 1;
if (i == n)
return 3 * n - 2 - j + 1;
if (j == 1)
return 4 * n - 4 - i + 2;
// 注意,递归的时候,n 要减 2 而不是减 1
return work(n - 2, i - 1, j - 1) + 4 * (n - 1);
}
真正做法,找规律找到通项公式
#include <bits/stdc++.h>
using namespace std;
int get(int x, int y, int n)
{
if (x <= y)
{
int k = (x < n - 1 - y) ? x : n - 1 - y;
return 4 * k * (n - k) + 1 + (x + y - k * 2);
}
int k = (y < n - 1 - x) ? y : n - 1 - x;
k = k + 1;
return 4 * k * (n - k) + 1 - (x + y - (k - 1) * 2);
}
int main()
{
int n, x, y;
cin >> n >> x >> y;
cout << get(x-1, y-1, n);
return 0;
}
5.6023: 神奇的幻方
NOIP2015提高组Day1T1
大力将题目的情况进行模拟,但是看到了一个有意思的模拟方法,巧妙利用取余可以省去很多步骤
#include <bits/stdc++.h>
using namespace std;
int n,a[40][40],x,y;
int main(){
scanf("%d",&n);
x=1,y=(n+1)/2;
for(int i=1;i<=n*n;i++){
a[x][y]=i;
if(!a[(x-2+n)%n+1][y%n+1]) x=(x-2+n)%n+1,y=y%n+1;
else x=x%n+1;//数学运算
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%d ",a[i][j]);
}
printf("\n");
}
}
6.6032: 玩具谜题
NOIP2016提高组Day1T1
一、先来往右边数
那么T的右边一个数的下标就是T+1,T的右边n个数的下标就是T+n啦!
我在访问队列的时候,要循环n次 每次T+=1 然后判断如果T>=len_a 则T-= len_a;循环n次 每次T+=1 然后T%=len_a;
公式为right(T)=(T+n)%len_a;
直接就找到了T右边第n个数的下标。
二、 然后往左边数了
同样的,那么第T个数的左边n个数的下标是:T-n
但是这样可能为负数。之前说过了,最多往左边数len_a-1人,再多数一个就回到原点。
则:T-(n%len_a) 有可能为负数 但是T-(n%len_a)+len_a绝对非负因为 (n%len_a) < len_a ,然后再mod上一个len_a防止超出即可。但是这样的话当 T = len_a 且 n=0 时 会出错
所以改成事先判断n是否为0就不会。
公式: left(T)=(T-(n%len_a)+len_a)%len_a
三、判断向哪一边数人
这个很简单。观察可知当代表方向的bool值与代表小人朝向的bool值不相等,则为顺时针(往左边),相等则为逆时针
#include <bits/stdc++.h>
using namespace std;
int n, m;
vector<string> name;
vector<bool> w;
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i++)
{
bool a;
string b;
cin >> a >> b; //读人
w.push_back(a);
name.push_back(b);
}
int point = 0; //下标从0开始数
while (m--) //循环m次的简写
{
bool f;
int num;
cin >> f >> num;
if (num != 0)
{
//异或符^,不等时为1否则为0。
if (w[point] ^ f)
point = (point + num) % n; //这里如果f和W不等,则顺时针
else
point = (point - num % n + n) % n; //否则逆时针
}
}
cout << name[point];
return 0;
}
7.5997: 时间复杂度
NOIP2017提高组Day1T2
这个题目很变态,需要处理得细节极多,令人窒息,考场遇见这种题目一定要想清楚再写
#include<cstdio>
#include<cstring>
using namespace std;
const int N=101,inf=1e9;
int st[N],val[N],top;
bool bz[N];
int read()
{
int X=0,w=1; char ch=0;
while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
return X*w;
}
int get()
{
char ch=getchar();
while((ch<'0' || ch>'9') && ch!='n') ch=getchar();
if(ch=='n') return inf;
int ret=ch-'0';
ch=getchar();
while(ch>='0' && ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret;
}
void readln()
{
char ch=getchar();
while(ch!='O' && ch!='F' && ch!='E') ch=getchar();
if(ch=='O')
{
getchar(),getchar();
}else
if(ch=='F')
{
getchar(),getchar();
get(),get();
}
}
int main()
{
int T=read();
while(T--)
{
int n=read();
if(n&1)
{
printf("ERR\n");
for(int i=0;i<=n;i++) readln();
continue;
}
getchar(),getchar();
char s=getchar();
int t=0,pd=top=0,mx=0;
memset(bz,false,sizeof(bz));
memset(st,0,sizeof(st));
memset(val,0,sizeof(val));
if(s=='n')
{
while(s<'0' || s>'9') s=getchar();
while(s>='0' && s<='9') t=t*10+s-'0',s=getchar();
}else s='0';
for(int i=1;i<=n;i++)
{
s=getchar();
while(s!='F' && s!='E') s=getchar();
if(s=='E')
{
bz[val[top--]]=false;
if(top<0)
{
for(int j=i+1;j<=n;j++) readln();
pd=2;
break;
}
continue;
}
while(s<'a' || s>'z') s=getchar();
if(bz[s-'a'+1])
{
for(int j=i+1;j<=n;j++) readln();
pd=2;
break;
}
bz[val[top+1]=s-'a'+1]=true;
int x=get(),y=get();
if(x>y)
{
st[++top]=-1;
continue;
}
if((x==inf && y==inf) || (x<=100 && y<=100) || st[top]<0)
{
st[top+1]=st[top];
top++;
}else
{
st[top+1]=st[top]+1;
top++;
if(st[top]>mx) mx=st[top];
}
}
if(pd==2 || top>0)
{
printf("ERR\n");
continue;
}
if(mx==t) printf("Yes\n"); else printf("No\n");
}
return 0;
}
本文来自博客园,作者:暴力都不会的蒟蒻,转载请注明原文链接:https://www.cnblogs.com/BobHuang/p/13867735.html