zoukankan      html  css  js  c++  java
  • 天天快乐编程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<<"
    ";
        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
    ",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("
    ");
    	}
    } 
    

    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
    ");
                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
    ");
                continue;
            }
            if(mx==t) printf("Yes
    "); else printf("No
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    LInux下几种定时器的比较和使用
    R中字符串操作
    GIS基本概念
    特征选择实践
    xcrun: error: invalid active developer path (/Applications/Xcode.app/Contents/Developer)解决办法
    mac os idea的快捷键
    python代码打包发布
    机器学习之聚类
    机器学习之决策树
    机器学习之逻辑回归
  • 原文地址:https://www.cnblogs.com/BobHuang/p/13867735.html
Copyright © 2011-2022 走看看