【纪中2020.2.16日】模拟赛题解

目录:

T1:Oliver的成绩
T2:方块纸
T3:团队背包
T4:神奇的项链

正题:

T1:Oliver的成绩

题目描述

Oliver进入初中后,觉得自己不能总是玩儿了,应该要好好学习了。正好一次考试结束了,Oliver想知道自己的语文,数学,英语分别与语文年级第一,数学年级第一,英语年级第一相差多少。由于Oliver所在年级有N个人,所以Oliver想你编个程序帮帮他。

输入

score.in共3N+4行,第一~三行分别为Oliver的语文数学英语成绩(位数M),第四行为N,以下3N行,每行一个数(它们的位数是M),分别为第N个同学的语文,数学,英语成绩。
即:Oliver的语文
Oliver的数学
Oliver的英语
N
第一个人的语文
数学
英语
第二个人的语文

输出

score.out共一行,有三个数,分别为Oliver的语文数学英语与年级第一的差。
如果Oliver是第一,则输出0.

样例输入

10 
10
10               
3 
0
80
0
40
0
0
0
0 
100

样例输出

30 70 90

分析:

这道题数据很大,要用高精度。比赛时不想敲,打算开longlong混分,但我忘了判断年级第一,导致只有30分
思路就是:找出最高单科分数,拿它减去oliver的成绩,如果oliver成绩最高,输出0

高精CODE:

#include<iostream>
#include<string>
#include<cstdio> 
#include<algorithm>
#include<cstring>
using namespace std;
string s1,s2,s3,t1,t2,t3,max1,max2,max3;
int n,a[50],b[50],sub[50],len;
string smax(string x,string y)  //找最高分数
{
    int lenx=x.size(),leny=y.size();
    if(lenx==leny)  //判断字符串长度
    {
        if(x<y)
            return y;
        else
            return x;
    }
    else if(lenx<leny) //长度不一
        return y;
    else
        return x;
}
void subtraction(string x,string y)  //减法函数
{
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(c,0,sizeof(c));
    int lenx=x.size(),leny=y.size();
    if(lenx>leny||lenx==leny&&x>y) //第一个分数高
    {
        for(int i=lenx-1;i>=0;i--)
            a[lenx-i-1]=x[i]-'0';  //转换
        for(int i=leny-1;i>=0;i--)
            b[leny-i-1]=y[i]-'0';
        len=lenx;
        for(int i=0;i<len;i++)
        {
            sub[i]+=(a[i]-b[i]);  //做减法
            if(sub[i]<0)  //借位
            {
                sub[i]+=10;
                sub[i+1]--;
            }
        }
        while(sub[len]==0)  //每一位都做减法
        {
            len--;
        }
    }
    else
    {
        sub[0]=0;
        len=0;
    }
}
int main()
{
	freopen("score.in","r",stdin);
	freopen("score.out","w",stdout);
    cin>>s1>>s2>>s3;
    max1=s1;max2=s2; max3=s3;
    cin>>n;
    while(n--)
    {
        cin>>t1>>t2>>t3;
        max1=smax(max1,t1);
        max2=smax(max2,t2);  //把每科最高分找出来
        max3=smax(max3,t3);
    }
    subtraction(max1,s1);  //减
    for(int i=len;i>=0;i--)
        cout<<sub[i];  //按位输出
    cout<<" ";
    subtraction(max2,s2); //减
    for(int i=len;i>=0;i--)
        cout<<sub[i];  //按位输出
    cout<<" ";
    subtraction(max3,s3);  //减
    for(int i=len;i>=0;i--)
        cout<<sub[i];  //按位输出
    cout<<endl;
    return 0;
}

T2:方块纸

题目描述

今天小D在他的课桌上玩方格纸,现在有一个平面直角坐标系,小D将方块纸放在这个坐标系中,并且方格纸的都与x轴、y轴平行,小D在这上面放了许多的方格纸,然后想知道对于平面直角坐标系中的一个点有多少个方格纸覆盖(包括方格纸的边和点),因为方格纸太多了,所以请聪明的你帮小D解决问题。

输入

第一行 一个正整数N,接下来N行 每行四个正整数x1,y1,x2,y2,分别表示方格纸左下角的坐标和右上角的坐标。
第n+2行一个正整数Q,接下来Q行 每行两个正整数x,y,表示询问点的坐标。

输出

一共Q行,表示对应坐标。

样例输入

3
1 1 5 5
2 2 6 6
3 1 4 3
2
2 2
4 3

样例输出

2
3 

分析:

这道题运用差分思想,还要求前缀和
30%的方法
对于每一个询问,枚举每个方格纸是否覆盖到。
时间复杂度O(N*Q)
100%的方法
因为方格纸所放在的平面直角坐标系的地方十分的小,只有(0,0)到(2000,2000),面积不足,所以只用将每个点被多少个方格纸覆盖算出来就可以了,直接一个一个加显然不可行,我们可以用差分的思想,把这张方格纸的四个角加上差分的系数,最后把前缀和求出来就可以了。
例如:一张方格纸左下角(0,0)右上角(1,1),对于这张方格纸需要变成这样
在这里插入图片描述
我们只需要
在这里插入图片描述
前缀和后就可以变成所需要的。

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std; 
long long int n,x1,x2,y1,y2,q,x,y,m,f[3010][3010],maxx=-1,maxy=-1; 
int main() 
{ 
	memset(f,0,sizeof(f)); 
	freopen("square.in","r",stdin); 
	freopen("square.out","w",stdout); 
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) 
	{ 
		scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2); 
		if(x2>=maxx) maxx=x2; 
		if(y2>=maxy) maxy=y2;   //找最大
		f[x1][y1]++; 
		f[x2+1][y2+1]++; 
		f[x1][y2+1]--;   //四个差分的角
		f[x2+1][y1]--; 
	} 
	for(int i=1;i<=maxx+1;i++) 
	{ 
		for(int j=1;j<=maxy+1;j++) 
		{	 
			f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+f[i][j];  //求前缀和
		}	 
	} 
	scanf("%lld",&m); 
	for(int i=1;i<=m;i++) 
	{ 
		scanf("%lld%lld",&x,&y); 
		printf("%lld\n",f[x][y]); 
	}	 
	return 0; 
}

T3:团队背包

题目描述

DaA 和他的朋友组成一个团队去旅行了。他们每个人都准备了一个背包,用来装旅行用的物品。他们的背包有两个特点:

  1. 每个人的背包能装无限多的物品,每种物品有一个价值,但只能装一件;
  2. 每个人都很有个性,所以每个人的背包不会完全相同。
    DaA 的团队中有M 个人,那么对于整个团队,背包价值和最大是多少呢?

输入

第一行两个整数M、N,表示团队的人数和物品的数量。
接下来一行N 个整数,表示每件物品的价值wi。
数据保证不会出现有空背包人的出现。

输出

一个整数,整个团队背包价值的最大值。

扫描二维码关注公众号,回复: 9384834 查看本文章

样例输入

Sample Input 1:

2 3
2 7 1

Sample Input 2:

8 4
1 2 3 4

样例输出

Sample Output 1:

19

Sample Output 2:

58

分析:

f[i]表示价值为 i 的背包的不同种数,可以用 DP 求出:
f[i] =∑ [i−w[j]]
然后从 ∑w[j] 开始从大到小枚举 f[i],则这 f[i]种背包的总价值为 f[i]*i。枚举
直到人数已经大于等于 M 的为止,统计一下答案就可以了。

CODE:

#include<cmath>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long LL;
LL sum,ans,n,m,a[1001],f[25501];
int main(){
	freopen("team.in","r",stdin);
	freopen("team.out","w",stdout);
	scanf("%lld%lld",&m,&n);
	sum=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		sum+=a[i];//把所有价值加起来
	}
	f[0]=1;
	for(int i=1;i<=n;i++)
	for(int j=sum;j>=0;j--)
	{
		if(j>=a[i]) f[j]=f[j]+f[j-a[i]]; //dp过程
		ans=0;
	}
	for(int i=sum;i>=0;i--)
		if(f[i]<=m)  //值为m个数>值为i个数
		{
			m-=f[i];  //减去个数
			ans=ans+f[i]*i;  //加上总价值
		}else{
			ans=ans+i*m;  //加到了m个
			break;
		}
	printf("%lld",ans);
return 0;
}

T4:神奇的项链

题目描述

从前有一条神奇的项链,为什么说它神奇呢?因为它有两个性质:

  1. 神奇的项链可以拉成一条线,线上依次是N 个珠子,每个珠子有一个能量值Ei;
  2. 除了第一个和最后一个珠子,其他珠子都满足Ei=(Ei-1+Ei+1)/2+Di。
    由于这条项链很长,我们只能知道其两端珠子的能量值。并且我们知道每个珠子的Di是多少。请聪明的你求出这N 个珠子的能量值分别是多少。

输入

第一行三个整数N、E1、EN,表示珠子个数N,第一个珠子和第N 个珠子的能量值。
第二行N-2 个整数,表示第2 个珠子到第N-1 个珠子的Di。

输出

输出仅一行,N 个整数,表示1 到N 个这N 个珠子各自的能量值Ei。
请放心,数据保证对于任意珠子满足(Ei-1+Ei+1)Mod 2=0

样例输入

Sample Input 1:

4 1 4
0 0

Sample Input 2:

10 1 22
1 2 -3 5 1 4 2 -1

样例输出

Sample Output 1:

1 2 3 4

Sample Output 2:

1 14 25 32 45 48 49 42 31 22

分析:

这道题我叫它递推不过分把……
看到好多dalao二分,而我只能找规律
它并没有给出我们e[i+1],所以我们要
因为e[i]=(e[i-1]+e[i+1])/2+d[i]
所以e[i]=(e[i-1]-d[i])*2-e[i-2]=e[i-1]*2-e[i-2]-d[i-1]*2
推一下可得:
e[3]=e[2]*2-e[1]-d[2]*2
e[4]=e[2]*3-e[1]*2-d[2]*4-d[3]*2
e[5]=e[2]*4-e[1]*3-d[2]*6-d[3]*4-d[4]*2

e[n]=e[2]* (n-1)-e[1](n-2)-d[2](n-2)*2-d[3] * (n-3)*2-…

解e[2]得e[2]=(e[n]+(d[2] *(n-2)*2+d[3] *(n-3) *2+…)+e[1] *(n-2))/(n-1)
再从头列举下去即可。

CODE:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
long long n,sum,e[500001],d[500001];
int main(){
	freopen("fett.in","r",stdin);
	freopen("fett.out","w",stdout);
    scanf("%lld%lld%lld",&n,&e[1],&sum);
    e[n]=sum;
    for(int i=2;i<n;i++){
        scanf("%lld",&d[i]);
        sum+=d[i]*(n-i)*2; //存和
    }
    sum=(sum+e[1]*(n-2))/(n-1);
    e[2]=sum;//推出的第二位
    printf("%lld %lld ",e[1],e[2]);  //先输出前两位
    for(int i=3;i<n;i++)
    {
    	e[i]=(e[i-1]-d[i-1])*2-e[i-2];  //第三位开始递推
    	printf("%lld ",e[i]);
	}
    printf("%lld",e[n]);  //最后一位
    return 0;
} 
发布了48 篇原创文章 · 获赞 34 · 访问量 4795

猜你喜欢

转载自blog.csdn.net/dgssl_xhy/article/details/104383149