NOIP2017普及組複賽解題報告
這次考試在衢州二中,這裡地形太複雜了,離開考前3分鐘才找到考場。無語·········所以一開考就沒有好心情。這次的題目相對來以往的前幾道題比較簡單,但最後一道題比較難,考場上只想出來了50分的程式碼。
時間限制:1s 記憶體限制:256M
第一題:成績 score
超級水題,NOIP史上最簡單的題沒有之一,只要c++如果入門的都會做,普通的運算子操作
難度:☆☆☆☆☆ (入門難度)
民間資料:100分
官方得分:100分
#include<bits/stdc++.h>
using namespace std;
int a,b,c;
int ans;
int main()
{
freopen ("score.in","r",stdin);
freopen ("score.out","w",stdout);
scanf ("%d %d %d",&a,&b,&c);
ans=a/5+b/10*3+c/2;
printf ("%d",ans);
return 0;
}
這道題對於c++的選手有一個無法避免的誤差,就是使用浮點數(float || double)是直接*0.3會出現t.9999999999········的情況。e.g. :10.0*0.3=2.9999999·····
這導致一些選手出現 printf (”%d”,int (ans))使結果少一,60分
第二題:圖書管理員 librarian
這道題其實也不難,暴力的時間複雜度也只有O(n*q)==O(1000000),而且圖書編碼與需求碼都用 int 存的下,唯一的難點就是判斷末尾是否相等,需要一定的程式碼能力
難度:★☆☆☆☆ (普及-)
難點:check 處理
民間資料:100分
官方成績:100分
#include<bits/stdc++.h>
using namespace std;
int n,q;
int a[1010];
int main()
{
freopen ("librarian.in" ,"r",stdin);
freopen ("librarian.out","w",stdout);
scanf ("%d%d",&n,&q);
for (int i=1;i<=n;i++)
scanf ("%d",&a[i]);
sort (a+1,a+n+1); //c++ STL
for (int i=1;i<=q;i++)
{
int l,x;
bool flag=0;
scanf ("%d %d",&l,&x);
int t=pow (10,l); //計算t 即末尾0的個數
for (int i=1;i<=n;i++)
if ((a[i]-x)%t==0) //重點:若x是a[i]的位數,那麼(a[i]-x)%t==0
{
flag=1;
printf ("%d\n",a[i]);
break;
}
if (!flag)
printf ("-1\n");
}
return 0;
}
這道題可以有變式,就是將字典編碼長度與查詢碼長度最大為100(注意是長度,不是數值),這樣對於排序就有點難了。
第三題:棋盤 chess
難度:★★☆☆☆ (普及剛好)
民間資料:100分
官方資料:100分
這道題程式碼量還是挺大的,考試時用記憶化搜尋做的,但據說最好的解決方案是廣度優先搜尋,但我的方法也AC了,記憶化搜尋相對來說好寫一點但是,需要很細心,在考場上
#include<bits/stdc++.h>
using namespace std;
int minn[110][110][2];
int n,m,ans=10000;
int a[110][110];
void DFS (int x,int y,int cos,bool way,int c)
{
if (minn[x][y][c]<=cos)
return;
minn[x][y][c]=cos;
if (x==m&&y==m)
{
ans=min (ans,cos);
return;
}
if (c==a[x+1][y])
DFS (x+1,y,cos,1,c);
else if (a[x+1][y]!=-1)
DFS (x+1,y,cos+1,1,a[x+1][y]);
else if (way)
{
if (c==1)
{
DFS (x+1,y,cos+2,0,1);
DFS (x+1,y,cos+3,0,0);
}
else
{
DFS (x+1,y,cos+2,0,0);
DFS (x+1,y,cos+3,0,1);
}
}
if (c==a[x-1][y])
DFS (x-1,y,cos,1,c);
else if (a[x-1][y]!=-1)
DFS (x-1,y,cos+1,1,a[x-1][y]);
else if (way)
{
if (c==1)
{
DFS (x-1,y,cos+2,0,1);
DFS (x-1,y,cos+3,0,0);
}
else
{
DFS (x-1,y,cos+2,0,0);
DFS (x-1,y,cos+3,0,1);
}
}
if (c==a[x][y+1])
DFS (x,y+1,cos,1,c);
else if (a[x][y+1]!=-1)
DFS (x,y+1,cos+1,1,a[x][y+1]);
else if (way)
{
if (c==1)
{
DFS (x,y+1,cos+2,0,1);
DFS (x,y+1,cos+3,0,0);
}
else
{
DFS (x,y+1,cos+2,0,0);
DFS (x,y+1,cos+3,0,1);
}
}
if (c==a[x][y-1])
DFS (x,y-1,cos,1,c);
else if (a[x][y-1]!=-1)
DFS (x,y-1,cos+1,1,a[x][y-1]);
else if (way)
{
if (c==1)
{
DFS (x,y-1,cos+2,0,1);
DFS (x,y-1,cos+3,0,0);
}
else
{
DFS (x,y-1,cos+2,0,0);
DFS (x,y-1,cos+3,0,1);
}
}
}
int main()
{
freopen ("chess.in","r",stdin);
freopen ("chess.out","w",stdout);
memset (minn,-1,sizeof (minn));
memset (a,-1,sizeof (a));
scanf ("%d%d",&m,&n);
for (int i=1;i<=n;i++)
{
int x,y,c;
scanf ("%d%d%d",&x,&y,&c);
a[x][y]=c;
}
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++)
{
minn[i][j][0]=10000;
minn[i][j][1]=10000;
}
DFS (1,1,0,1,a[1][1]);
if (ans==10000)
{
printf ("-1");
return 0;
}
printf ("%d",ans);
return 0;
}
第四題:跳房子 jump
第一眼看到資料大小就想到了二分答案,然後在check函式中就想到了用動態規劃(DP)來做,但這是O(N^2)的演算法肯定會超時,但得50分已經可以了,但二分寫錯了,這是最噁心的地方,只得了20分
標準程式是用二分答案+單調佇列來做的,略微超綱了,那是提高組的演算法,是比較難理解的
難度:★★★★☆ (提高+)
民間資料:20分
官方得分:20分
20分程式碼
#include<bits/stdc++.h>
using namespace std;
int n,d,k;
int x[500010],s[500010];
int sum=0,ans;
bool check (int u)
{
int f[500010];
memset (f,0,sizeof (f));
f[0]=0;
int ans=-1000000010;
for (int i=0;i<=n;i++)
{
int l1=x[i]+d-u;
if (l1<=x[i])
l1=x[i]+1;
int r1=x[i]+d+u;
if (r1>=x[n])
r1=x[n];
for (int j=i+1;j<=n;j++)
if (x[j]>=l1&&x[j]<=r1)
{
f[j]=max (f[j],f[i]+s[j]);
ans=max (ans,f[j]);
}
}
if (ans>=k)
return 1;
else
return 0;
}
int main()
{
//freopen ("jump.in","r",stdin);
//freopen ("jump.out","w",stdout);
scanf ("%d%d%d",&n,&d,&k);
x[0]=0;
for (int i=1;i<=n;i++)
{
scanf ("%d%d",&x[i],&s[i]);
if (s[i]>0)
sum+=s[i];
}
if (sum<k)
{
printf ("-1");
return 0;
}
int l=0,r=1000000010;
while (l<r)
{
int mid=(l+r)/2;
if (check (mid))
{
r=mid-1;
ans=mid;
}
else
l=mid+1;
}
printf ("%d",ans);
return 0;
}
50分程式碼
#include<bits/stdc++.h>
using namespace std;
int n,d,k;
int x[500010],s[500010];
int sum=0,ans;
int f[500010];
bool check (int u)
{
memset (f,0,sizeof (f));
f[0]=0;
int ans=-1000000010;
for (int i=0;i<=n;i++)
{
int l1=x[i]+d-u;
if (l1<=x[i])
l1=x[i]+1;
int r1=x[i]+d+u;
if (r1>=x[n])
r1=x[n];
for (int j=i+1;j<=n;j++)
if (x[j]>=l1&&x[j]<=r1)
{
f[j]=max (f[j],f[i]+s[j]);
ans=max (ans,f[j]);
if (ans>=k)
return 1;
}
}
if (ans>=k)
return 1;
else
return 0;
}
int main()
{
//freopen ("jump.in","r",stdin);
//freopen ("jump.out","w",stdout);
scanf ("%d%d%d",&n,&d,&k);
x[0]=0;
for (int i=1;i<=n;i++)
{
scanf ("%d%d",&x[i],&s[i]);
if (s[i]>0)
sum+=s[i];
}
if (sum<k)
{
printf ("-1");
return 0;
}
int l=0,r=1000000010;
while (l<r)
{
int mid=(l+r)/2;
if (check (mid))
r=mid;
else
l=mid+1;
}
printf ("%d",l);
return 0;
}
AC程式碼 單調佇列
#include<bits/stdc++.h>
using namespace std;
struct node{
int x,v;
}q[500005];
int f[500005];
long long maxx=0;
int n,d,k;
int dis[500005],sc[500005];
bool check (int g)
{
int low=max (1,d-g),high=d+g;
int cur=0,head=0,tail=-1;
memset (f,0,sizeof (f));
for (int i=1;i<=n;i++)
{
for (;cur<i&&dis[cur]<=dis[i]-low;cur++)
{
while (head<=tail&&q[tail].v<f[cur])
tail--;
if (f[cur]!=-0x3f3f3f3f)
q[++tail].v=f[cur],q[tail].x=dis[cur];
}
while (head<=tail&&dis[i]-q[head].x>high)
head++;
f[i]=(head<=tail)?q[head].v+sc[i]:-0x3f3f3f3f;
if (f[i]>=k)
return 1;
}
return 0;
}
int main()
{
scanf ("%d%d%d",&n,&d,&k);
for (int i=1;i<=n;i++)
{
scanf ("%d%d",&dis[i],&sc[i]);
maxx+=max (sc[i],0);
}
if (maxx<k)
{
printf ("-1");
return 0;
}
int l=1,r=dis[n];
while (l<r)
{
int mid=(l+r)/2;
if (check (mid))
r=mid;
else
l=mid+1;
}
printf ("%d",l);
return 0;
}