NOIP2017普及組 解題報告
阿新 • • 發佈:2019-02-12
前言
好吧,由於賽時本人還是一個蒟蒻,(雖然現在也是),導致一直沒有做完後面兩題。。。
先說下賽時分數吧。。。
前兩題日常水過,後兩題日常放棄,其實第三題有想著做但是沒時間了
第一題 | 第二題 | 第三題 | 第四題 |
---|---|---|---|
Accepted | Accepted | WA | WA |
。。。
解題報告
第一題 成績
連結
大意
輸入計算
思路
模擬,由於的強制轉換很騷,所以就要注意一下
時間複雜度和空間複雜度都是
程式碼
#include<cstdio>
#define sr c=getchar()
#define input read()
#define pd (c<'0'||c>'9')
using namespace std;
int read()//樓樓超醜的程式碼,莫介意
{
int d=1,f=0;char c;
while (sr,pd) if (c=='-') d=-1;f=f*10+c-48;
while (sr,!pd) f=f*10+c-48;
return d*f;
}
int main()
{
int x,y,z;
x =input;y=input;z=input;
int a,b,c;
a=x*20;b=y*30;c=z*50;
int ans=(a+b+c)/100;
printf("%d",ans);//防止強制轉換出現的bug
}
第二題 圖書管理員
連結
大意
忘了(真的忘了。。。,真的不是我懶)
思路
重點在%,模擬,本人程式碼奇醜無比,奇low無比(當時是真的蒟蒻)
時間複雜度:
空間複雜度:
(醜到懷疑人生)
程式碼
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define sr c=getchar()
#define input read()
#define pd (c<'0'||c>'9')
using namespace std;
int n,m;
int book[1011]; int z[1011];
int numb[111];//一群醜陣列
int len;
bool ok;
bool cmp(int x,int y)//一個醜排序
{
return x<y;
}
bool pds(int x)//一個醜判斷
{
int j=0;
while (x>0)
{
j++;
z[j]=x%10;
x/=10;
//printf("%d",z[j]);
}
int i=0;
if (len>j) return false;
for (int k=len;k>0;k--)
{
i++;
if (z[i]!=numb[k]) return false;
}
return true;
}
int read()//一個醜輸入流
{
char c;int d=1,f=0;
while (sr,pd) if (c=='-') d=-1;f=f*10+c-48;
while (sr,!pd) f=f*10+c-48;
return d*f;
}
int main()
{
n=input;m=input;
for (int i=1;i<=n;i++)
book[i]=input;//一個醜輸入
sort(book+1,book+1+n,cmp);//醜排序
for (int i=1;i<=m;i++)
{
len=input;char lc;//memset(numb,0,sizeof(numb));
for (int j=1;j<=len;j++)
{
lc=getchar();
numb[j]=lc-48;
}ok=false;//醜輸入
for (int j=1;j<=n;j++)
{
if (pds(book[j])) {ok=true;printf("%d\n",book[j]);break;}//醜查詢
}
if (!ok) printf("-1\n");//醜判斷
}
return 0;//醜return
}
終於醜完了
第三題 棋盤
連結
大意
有一個的棋盤,有個色塊是有色的。
有色的色塊間有兩種規則
- 若兩個格子顏色相同,則這兩個格子之間行走的代價為0
- 若兩個各自顏色不同,則這兩個格子之間行走的代價為1
有色和無色或無色和有色間有一種規則
可以施展膜拜大法(魔法),將無色的方格變成有色的方格的顏色,代價為2
現求從左上角走到右下角的最小花費,若不能到達,輸出-1
思路
暴搜會超時,解法很多,這裡列舉一下
- bfs
- dfs+剪枝
- dp
最短路(這個很複雜,洛谷上有詳細介紹,這裡主要講dfs)
其實dfs很容易理解,就是開一個表示是否有使用魔法,也可以直接在裡面加一個來判斷,效果是一樣的
值得注意的是,走過的路還能再走,所以不用判斷是否已經走過,而是儲存最優解
時間複雜度:(應該是吧)
空間複雜度:(若使用flag作為一個引數,空間複雜度可一將為)
程式碼
終於不是奇醜無比了,當然在面前永遠是醜的。。。
// luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
#define r(i,a,b) for(int i=a;i<=b;i++)
#define check(x,y) (x>=1&&x<=n&&y>=1&&y<=n)//判斷是否在期盼內
using namespace std;int n,m,ans=536870912;
const short dx[4]={-1,0,1,0};
const short dy[4]={0,1,0,-1};//四個方向
bool use[101][101];int color[101][101],f[101][101];//use為是否使用魔法,color表示此點的顏色,f表示最優解
LL read()//輸入流
{
char c;int f=0,d=1;
while((c=getchar())<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
while((c=getchar())>=48&&c<=57)f=(f<<3)+(f<<1)+c-48;
return d*f;
}
void dfs(int x,int y,int now)
{
if(!check(x,y)||now>=f[x][y]) return;//超出範圍或者不是最優則退出
if(x==n&&y==n) {ans=min(now,ans);return;}f[x][y]=now;//儲存
r(i,0,3)
{
int qx=x+dx[i],qy=y+dy[i];//獲取位置
if(check(qx,qy))//判斷
{
if(use[x][y]&&!color[qx][qy]) continue;//若已經使用魔法但是目標格子是空的那麼直接返回
if(color[qx][qy])
if(color[x][y]==color[qx][qy]) dfs(qx,qy,now);//顏色相同
else dfs(qx,qy,now+1);//不相同
else
if(!use[x][y]&&!color[qx][qy])//使用魔法
{
use[qx][qy]=true;
color[qx][qy]=color[x][y];//標記已經使用並且改變顏色
dfs(qx,qy,now+2);
use[qx][qy]=false;
color[qx][qy]=0;//回溯
}
}
}
}
int main()
{
memset(f,127/3,sizeof(f));
n=read();m=read();
r(i,1,m) color[read()][read()]=read()+1;//輸入,為了更好區分,所以每個格子都+1,這樣區分開了紅色和白色的格子
dfs(1,1,0);//搜尋
if(ans==536870912) puts("-1");else printf("%d",ans);//輸出
}
第四題 跳房子
連結
大意
在一根數軸上有個點,給出它們距離原點的距離以及它們的價值,現在有一個機器人,他每次可以向右邊走個單位長度,但是為了的得到一定的價值,需要改進這個機器人。已知花費點金幣可以使它能跳的距離變成
之間,問至少需要多少金幣可以拿到點價值
思路
首先可以想到若使用枚金幣可以,那麼使用枚也必然可以,所以這就滿足二分的條件
至於如何確定二分的答案是否正確呢?需要用到動態規劃,先給出方程
當然,這個轉移是一定要滿足可以跳到這個位置的情況下的,時間複雜度為會超時
所以要用到單調佇列優化
先簡單提一下單調佇列,例如現在此佇列為
9 8 7 6 5 2 1
現在我要插入7,則單調佇列變成
9 8 7 而不是 9 8 7 6 5 2 1 7
時間複雜度:
空間複雜度:
程式碼(STL)
#include<cstdio>
#include<algorithm>
#include<deque>
#define LL long long
#define r(i,a,b) for(int i=a;i<=b;i++)
#define N 500001
using namespace std;LL n,d,k,l,r,mid,far[N],num[N],ans=-1,dp[N];
LL read()
{
char c;int f=0,d=1;
while((c=getchar())<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
while((c=getchar())>=48&&c<=57)f=(f<<3)+(f<<1)+c-48;
return d*f;
}
bool check(int money)
{
deque<int>q;
far[0]=0;
fill(dp,dp+n,0);//初始化
int high=money+d,low=max(d-money,(LL)1);//記得末尾是1
int j=0;
r(i,1,n)
{
while(far[i]-far[j]>=low)
{
while(!q.empty()&&dp[j]>=dp[q.back()]) q.pop_back();//彈出去
q.push_back(j++);//放進來
}
while(!q.empty()&&far[i]-far[q.front()]>high) q.pop_front();//彈出去
if(q.empty())dp[i]=-9999999999;//空了
else dp[i]=dp[q.front()]+num[i];//沒空
if(dp[i]>=k) return 1;//判斷
}
return 0;
}
int main()
{
n=read();d=read();k=read();
r(i,1,n)
far[i]=read(),num[i]=read();
int l=1,r=N<<5;//範圍
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;else l=mid+1;//二分
}
printf("%lld",ans);
}
程式碼(單調佇列)
#include<cstdio>
#include<algorithm>
#define LL long long
#define r(i,a,b) for(int i=a;i<=b;i++)
#define N 500001
using namespace std;LL n,d,k,l,r,mid,far[N],num[N],ans=-1,dp[N];
LL read()
{
char c;int f=0,d=1;
while((c=getchar())<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-