2016普及组已经考完了,我也写了四对,八个各种姿势的“freopen”。
废话不多说:
题目链接在此:NOIP2016普及组
第一题:铅笔(pencil):
初步判断水题,同学说算价格时用for循环,RX:
#include <cstdio>
using namespace std;
int n,a,b,val=99999999;
int main(){
scanf("%d",&n);
for (int i=1; i<=3; i++) {
scanf("%d%d",&a,&b);
int k=0,s=0;
for (; k<n; k+=a,s+=b);
if (s<val) val=s;
}
printf("%d",val);
return 0;
}
然而聪明人都用我的做法:
#include<cstdio>
#include<algorithm>
using namespace std;
float n,w[10];
int val=999999999,v[10];
int main(){
//freopen("pencil.in","r",stdin);
//freopen("pencil.out","w",stdout);
scanf("%f",&n);
for(int i=1;i<=3;i++){
scanf("%f%d",&w[i],&v[i]);
val=min(int(n/w[i]+0.9999)*v[i],val);//精髓之处
}
printf("%d",val);
return 0;
}
第二题:回文日期(date):
也是水题啊:
爆搜都不会超时,也就O(10^8)
从起始日期到结束日期一天一天搜,就行啦,RX:
#include<cstdio>
#include<algorithm>
using namespace std;
int date[2],m[13]={0,31,28,31,30,31,30,31,31,30,31,30,31},ans;
bool R(int r){ return (r%4==0&&r%100!=0)||r%400==0; }
int nextmonth(int r){ return r==12?1:r+1; }
bool round(int r){
char a[8];
int i=0;
while(r){
a[i++]=r%10;
r/=10;
}
for(int i=0;i<=3;i++)
if(a[i]!=a[7-i])
return 0;
return 1;
}
int nextday(int x){
int year,mon,day;
day=x%100;
mon=x%10000/100;
year=x/10000;
if(mon==2&&day==28&&R(year))day++;
else if(day>=m[mon]) mon=nextmonth(mon),day=1;
else day++;
if(day==1&&mon==1) year++;
return year*10000+mon*100+day;
}
int main(){
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
for(int i=0;i<=1;i++){
scanf("%d",&date[i]);
}
for(int i=date[0];i<=date[1];i=nextday(i))
if(round(i))
ans++;
printf("%d",ans);
return 0;
}
另外还可以枚举年,因为一年最多只有一个回文日期,起始年和终止年特殊处理就行了
第三题:海港(port)
这是一道模拟题,我用队列做,但明显超时
我的思路是这样的,来一个人就入队,来一条船就让他们出队,搜答案。
但这样有一个缺点,一天内的人还得重新入队,如果10000条船挤在一天进港,
简直妙不可言。
正解是加一个数组记录岛上每种国籍有多少人,令每个人在岛上待一天就走,
船入港,人入队,别的船入港,就判断别的船的入港时间与人的入港时间相差超不超过一天,如果超过,人就出队,不然,现在岛上的不同国籍数就是答案。
正解RX:
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
struct node{
int nation,t;
node(){}
node(int a,int b){
nation=a;
t=b;
}
};
queue<node>q;
int n,t,k,c[100005],sum,a;
int main(){
//freopen("port.in","r",stdin);
//freopen("port.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&t,&a);
for(int j=1;j<=a;j++){
scanf("%d",&k);
q.push(node(k,t));
c[k]++;
if(c[k]==1)
sum++;
}
while(q.front().t<=t-86400){
c[q.front().nation]--;
if(!c[q.front().nation]) sum--;
q.pop();
}
printf("%d\n",sum);
}
}
第四题:魔法阵(magic)
这题就显得高大上了,40000,15000的范围令人不敢恭维,不过,还是暴搜。
50分做法:
就是搜,搜四个数,并判断可不可行,并且先对魔法物品按魔法值排序,再枚举。
#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
int m,n,ans[40001][4];
struct node{
float a;
int ID;
bool operator < (const node next)const{
return a<next.a;
}
}w[40001];
int main(){
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%f",&w[i].a);
w[i].ID=i;
}
sort(w+1,w+1+m);
for(int a=1;a<=m-3;a++)
for(int b=a+1;b<=m-2;b++){
if(w[b].a==w[a].a) continue;
for(int c=b+1;c<=m-1;c++){
if(w[b].a==w[c].a) continue;
if(w[c].a>4*w[b].a-3*w[a].a)
for(int d=c+1;d<=m;d++){
if(w[c].a==w[d].a)continue;
if(w[b].a-w[a].a==2*w[d].a-2*w[c].a)
{
ans[w[a].ID][0]++;
ans[w[b].ID][1]++;
ans[w[c].ID][2]++;
ans[w[d].ID][3]++;
}
}
}
}
for(int i=1;i<=m;i++){
for(int j=0;j<3;j++)
printf("%d ",ans[i][j]);
printf("%d\n",ans[i][3]);
}
return 0;
}
65分做法:
#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
int m,n,ans[40001][4];
struct node{
float a;
int ID;
bool operator < (const node next)const{
return a<next.a;
}
}w[40001];
int main(){
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%f",&w[i].a);
w[i].ID=i;
}
sort(w+1,w+1+m);
for(int a=1;a<=m-3;a++)
for(int b=a+1;b<=m-2;b++){
if(w[b].a==w[a].a) continue;
for(int c=b+1;c<=m-1;c++){
if(w[b].a==w[c].a) continue;
if(w[c].a>4*w[b].a-3*w[a].a)
for(int d=c+1;d<=m;d++){
if(w[c].a==w[d].a)continue;
if(w[b].a-w[a].a<2*w[d].a-2*w[c].a) break;//注意这行
if(w[b].a-w[a].a==2*w[d].a-2*w[c].a)
{
ans[w[a].ID][0]++;
ans[w[b].ID][1]++;
ans[w[c].ID][2]++;
ans[w[d].ID][3]++;
}
}
}
}
for(int i=1;i<=m;i++){
for(int j=0;j<3;j++)
printf("%d ",ans[i][j]);
printf("%d\n",ans[i][3]);
}
return 0;
}
其实就是加了个剪枝条件,但效果很好,速度快了接近一半。
85分做法:
#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
char rBuf[1<<25],pBuf[1<<25];
char *rbuf=rBuf,*pbuf=pBuf;
int getint()
{
int x=0;
while(*rbuf<'0'||*rbuf>'9')rbuf++;
while(*rbuf>='0'&&*rbuf<='9')x=x*10+(*rbuf++-'0');
return x;
}
void putint(int x)
{
if(x>9)putint(x/10);
*pbuf++=x%10+'0';
}
const int N=15005,M=40005;
int n,m,x[M],ans[N][4],s[N];
void output()
{
for(int i=1;i<=m;i++)
{
for(int j=0;j<4;j++)
{
putint(ans[x[i]][j]);
*pbuf++=' ';
}
*pbuf++='\n';
}
fwrite(pBuf,pbuf-pBuf,1,stdout);
}
int main()
{
fread(rBuf,1,1<<25,stdin);
n=getint();m=getint();
for(int i=1;i<=m;i++)
s[x[i]=getint()]++;
for(int a=1;a<=n;a++)
if(s[a])
for(int b=a+1;b<=n;b++)
if(s[b])
for(int c=max(b+1,4*b-3*a+1);c<=n;c++)
if(s[c])
{
if(((b-a+2*c)&1)==0)
{
int d=(b-a+2*c)>>1;
if(d>c&&d<=n&&s[d])
{
ans[a][0]+=s[b]*s[c]*s[d];
ans[b][1]+=s[a]*s[c]*s[d];
ans[c][2]+=s[a]*s[b]*s[d];
ans[d][3]+=s[a]*s[b]*s[c];
}
}
}
output();
return 0;
}
这个算法是三个for循环,就是根据:
Xb-Xa=2(Xd-Xc);
消掉一个元,枚举余下的3个就行啦。
AC正解!!!:
为什么题目告诉你”每个魔法值Xi是不超过n的正整数,可能有多个物品的魔法值相同。”
正解正是利用了这句话,将40000个物品分为15000个重量各不相同的物品。
然后用一个sam[15001]记录每个重量有多少个物品。
另外,我们设
Xd-Xc=i(i<=n/9);
可得Xb-Xa=2i, Xc-Xb>6i;
因此外层循环枚举i,
内层第一个循环枚举B,C的距离J
可得:d[i*3+J]+=w[i*2]*w[i*3]*w[i*2+J]
以此类推,代码RX:
#include<cstdio>
#include<algorithm>
#define L 20000
using namespace std;
int a[L],b[L],c[L],d[L];
int w[40001],sam[15001],n,m;
int main()
{
//freopen("magic.in","r",stdin);
//freopen("magic.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d",&w[i]);
sam[w[i]]++;
}
for(int i=1;i<=n/9;i++){
int loc_d_least=i*9+1,possiblity_a_b=0;
for(int j=i*9+2;j<=n;j++){
possiblity_a_b+=sam[j-loc_d_least]*sam[j-loc_d_least+2*i];
d[j]+=possiblity_a_b*sam[j-i];
c[j-i]+=possiblity_a_b*sam[j];
}
int possiblity_c_d=0;
for(int j=n-9*i-1;j>=1;j--){
possiblity_c_d+=sam[j+loc_d_least]*sam[j+loc_d_least-i];
a[j]+=possiblity_c_d*sam[j+2*i];
b[j+2*i]+=possiblity_c_d*sam[j];
}
}
for(int i=1;i<=m;i++)
printf("%d %d %d %d\n",a[w[i]],b[w[i]],c[w[i]],d[w[i]]);
return 0;
}