目录:
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 和他的朋友组成一个团队去旅行了。他们每个人都准备了一个背包,用来装旅行用的物品。他们的背包有两个特点:
- 每个人的背包能装无限多的物品,每种物品有一个价值,但只能装一件;
- 每个人都很有个性,所以每个人的背包不会完全相同。
DaA 的团队中有M 个人,那么对于整个团队,背包价值和最大是多少呢?
输入
第一行两个整数M、N,表示团队的人数和物品的数量。
接下来一行N 个整数,表示每件物品的价值wi。
数据保证不会出现有空背包人的出现。
输出
一个整数,整个团队背包价值的最大值。
样例输入
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:神奇的项链
题目描述
从前有一条神奇的项链,为什么说它神奇呢?因为它有两个性质:
- 神奇的项链可以拉成一条线,线上依次是N 个珠子,每个珠子有一个能量值Ei;
- 除了第一个和最后一个珠子,其他珠子都满足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;
}