2016PJ题解
1、pencil
算法一
题目要求只能买一种包装的铅笔。可以发现,题目有整数倍的子任务,我们只需要看看3种包装每种最多要买多少袋同样包装的铅笔,再乘上它对应的价钱,取3者最小值即可
期望得分:50
算法二
针对于一算法。例:一包装每袋有2支铅笔,一袋3元,老师共需要3只铅笔.按照一方法做是不行的,因为买完后,还是有3 mod 2=1支铅笔,所以我们还要买多一个包装的。除此之外,其余同上。
期望得分:100
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <map>
using namespace std;
int n;
int main()
{
freopen("pencil.in","r", stdin);
freopen("pencil.out","w",stdout);
intx, y, z, ans = 0x3f3f3f;
for(inti = 1; i <= 3; i++)
{
scanf("%d5d",&x,&y);
z= n / x;
if(n % x != 0)
z +=1;
if(z* y < ans)
ans = z * y;
}
printf("%d",ans);
return0;
}
2、date
算法一
子任务中说date1=date2,所以我们直接判断题目输入的字符串是否构成回文,输出即可。
时间复杂度O(8) 期望得分:60
算法二
可以在题目两个日期之间date1~date2一天一天判断当天的日期是否构成回文,用字符串处理会超时,用算术法应能卡过去。
时间复杂度O(8999*12*31) 如果是算术法的话
期望得分:80~100
算法三
因为年份是四位数,那么组成回文数时,回文数的左边一定是这个年份,枚举年份即可。根据回文数的左边,我们就可以推出右边的月和日。只需要判断月和日合不合法。
1、日是否超过当前这个月的天数。
2、月份只在1~12间,
3、闰年的情况
4、且当前日期是否在date1~date2之间。
时间复杂度O(8999*4) 期望得分:100
#include<cstdio>
inta[13]={0,31,29,31,30,31,30,31,31,30,31,30,31},s,t,y1,m1,d1,y2,m2,d2,ans=0,y;
int f(int x)
{
intj=0,k=1;
if(x<10)k=10;
for(;x;x/=10)j=j*10+(x%10);
returnj*k;
}
int main()
{
freopen("date.in","r",stdin);
freopen("date.out","w",stdout);
scanf("%d\n%d",&s,&t);
for(inti = 1; i <= 12; i++)
for(int j = 1; j <= a[i]; j++)
{
y=f(j)*100+f(i);
if(y*10000+i*100+j>=s&&y*10000+i*100+j<=t&&f(y)==i*100+j)ans++;
}
printf("%d",ans);
}
3、port
算法1:直接根据题目模拟 空间或者时间会爆掉
期望得分:60
算法2:采用队列:
用一个结构体队列存每个人来的时间和他的国籍,用一个vis数组存每个人来的次数,是第一次来sum便加一。
然后从前面第一个人开始扔下船,直到第一个人与当前这艘船相差没超过1天即可,每扔一个人便vis[此人国籍]减一,当这个人来的次数为0时,sum减一。
就这样,时间和空间都不会爆
期望得分:100
#include<cstdio>
#include<queue>
using namespace std;
struct p
{
int pt,pn;//存每个人的时间和国籍
};
queue <p> ship;
int n,vis[100005],sum;
int main()
{
scanf("%d",&n);
while(n--)
{
int t,m,_n;
scanf("%d%d",&t,&m);//船来的时间和人数
for(int i=1;i<=m;i++)
{
scanf("%d",&_n);
p x;
x.pt=t;
x.pn=_n;
ship.push(x);//将这个人存入队列
vis[_n]++;
if(vis[_n]==1)//如果是第一次来
sum++;
}
while(1)
{
int a,b;
a=ship.front().pt,b=ship.front().pn;//检查这个人
if(t-a<86400)//与当前间隔没超过一天
break;
vis[b]--;
if(vis[b]==0)
sum--;
ship.pop();//把这个人扔出去
}
printf("%d\n",sum);//输出sum
}
}
4 Magic
算法一
枚举四个数,符合条件累加方案数。
时间复杂度O(n^4或m^4) 期望得分:35~45
算法二
第四个数是可以根据xb-xa=2(xd-xc)推出来的,所以减少一重循环。
时间复杂度(n^3或m^3) 期望得分:50~75
算法二
其实我们可以根据题目的三个条件画出一个数轴
①A<B<C<D
②(B-A)=2(D-C)
③3(B-A)<(C-B)
根据①条件:
根据②条件,我们可以得出,AB的距离是CD距离的两倍,设CD距离为i:
根据③条件,我们可以得出CB的距离大于AB的三倍,就是>2i*3=6i
我们可以枚举i,再枚举A的位置,枚举C的位置,再判断一下是否出现过ABCD,累加方案即可(Wx=Wy*Wz*Wa)另外三个出现次数。
时间复杂度O(**) 期望得分:80~85
4.3 算法三
分析题目,列出条件:Xa<Xb<Xc<Xd,Xb-Xa=2(Xd-Xc),Xb-Xa<(Xc-Xb)/3
遇到这种有条件的题,通常把图形画出来比较直观。
如图所示,若把d点确定,设c-d距离为i,则a,b的距离就是2i,则b,c的距离>3*(a-b)也就是>6i,总距离大于9i,那么我们的外层循环就枚举i,再枚举d的位置,d的方案数就等于(前面所有a的方案)*(前面所有b的方案)*(当前c的方案数),c的方案数=(前面所有a的方案)*(前面所有b的方案)*(当前d的方案数),同理,枚举a的位置,也可以得到a与b的方案数。
时间复杂度O(n) 期望得分:95~100
1. #include<cstdio>
2. #define M 15005
3. int a[M],b[M],c[M],d[M],w[3*M],h[3*M];
4. int n,m,i,j,x,s;//s就是上面的y,累加和
5. int main()
6. {
7. scanf("%d%d",&n,&m);
8. for(i=1;i<=m;i++)
9. {
10. scanf("%d",&h[i]);
11. w[h[i]]++;
12. }
13. for(i=1;9*i<n;i++)//注意是总长度>9*i,边界一定要考虑无误,想通为什么
14. {
15. x=9*i+1;s=0;//设边界最好画个草图自己算一算
16. for(j=9*i+2;j<=n;j++)
17. {
18. s+=w[j-x]*w[j-x+2*i];
19. d[j]+=s*w[j-i];
20. c[j-i]+=s*w[j];
21. }
22. s=0;
23. for(j=n-x;j>=1;j--)//注意循环不能顺序,因为s的累加和会改变,a[j]会加上后面的c,d,而不是前面的
24. {
25. s+=w[j+x]*w[j+x-i];
26. a[j]+=s*w[j+2*i];
27. b[j+2*i]+=s*w[j];
28. }
29. }
30. for(i=1;i<=m;i++)
31. printf("%d %d %d %d\n",a[h[i]],b[h[i]],c[h[i]],d[h[i]]);
32. }