NOIP2017提高組DAY1題解
阿新 • • 發佈:2019-01-29
T1:小凱的疑惑
考察知識:數學,數論
演算法難度:XXX 實現難度:X
分析:這是一個推(cai)結(da)論(an)的題
一看資料範圍,就知道應該用時間複雜度或以下的演算法,如果猜有些或許你會發現答案就是,注意用long long
程式碼:
#include<iostream>
int main(){
long long a,b;
std::cin>>a>>b;
std::cout<<a*b-a-b;
return 0;
}
T2:時間複雜度
考察知識:模擬,棧
演算法難度:XXX 實現難度:XXX+
分析:一個在NOIP中中等規模的模擬類題目
大致思路:我們採用棧結構動態儲存每一層的時間複雜度,並在當前層更新時間複雜度。
這道題保證,更加簡化了這道題的難度
程式碼:
#include<map> #include<cstdio> #include<string> #include<cstring> #include<algorithm> using namespace std; map<string,int>mp; int T,L,top,cur[105]; char O[255],F[255],var[105][255],x[255],y[255]; void check(){ int index=0,index2=0,index_now=0;//時間複雜度指數 bool CE=false; memset(cur,0,sizeof(cur)); top=0,cur[0]=1,mp.clear(); scanf("%d%s",&L,O); for(int i=1;i<=L;i++){ scanf("%s",F); if(F[0]=='F'){ scanf("%s%s%s",var[++top],x,y); if(mp[var[top]]==2018) CE=true;//判斷變數是否重名 else mp[var[top]]=2018; int X=0,Y=0;//迴圈次數 if(x[0]=='n') X=2018; else for(int i=0;x[i]!='\0';i++) X=X*10+x[i]-'0'; if(y[0]=='n') Y=2018; else for(int i=0;y[i]!='\0';i++) Y=Y*10+y[i]-'0'; if(X>Y||cur[top-1]==0) cur[top]=0;//無法進入更深的迴圈 else if((X<=Y&&Y<2018)||X==Y) cur[top]=1;//當前時間複雜度O(1) else cur[top]=2;//O(n) // if(cur[top-1]==0) cur[top]=0; if(cur[top]==2) index=max(index,++index_now);//當前最大時間複雜度 } else { if(cur[top]==2) index_now--; mp[var[top--]]=0;//銷燬變數 } } if(top||CE) {printf("ERR\n");return;} if(O[2]=='n'){ for(int i=4;isdigit(O[i]);i++) index2=index2*10+O[i]-'0'; } if(index==index2) printf("Yes\n"); else printf("No\n"); } int main(){ scanf("%d",&T); while(T--) check(); return 0; }
T3:逛公園
考察知識:圖論,記憶化搜尋
演算法難度:XXXX 實現難度:XXXX
分析:
遇到難題先看資料範圍,有3個的點,就是最短路徑計數模板,所以我們至少可以得30分。
對於非搜尋類難題,資料範圍就算小也不一定想得出部分分演算法,但是注意到這道題,我們可以考慮用記憶化的方法儲存每個點比最短路大k的路徑數,然後採用記憶化搜尋即可。
這種方法類似於動態規劃,所以我們還是用動態規劃一般套路來描述:
定義:表示節點 i 到 n 所有路徑中路徑長度小於等於最短路徑加 k 的路徑數
狀態轉移:
設V是節點 i 可以直接到達的節點的集合,表示 i 到 n 的最短路徑長度,表示節點 i,j 的距離
則有:
邊界:至少等於1
注意事項:
1.由於有多組資料,每次處理完後要初始化
2.由於部分資料含0邊,所以我們設一個陣列,表示正在被計算,如果繼續搜尋又達到 (i,k)說明有無窮多種情況
程式碼:
#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char in_c;
template<typename T>
void scan(T &in_n){
for(in_c=getchar();in_c<'0'||in_c>'9';in_c=getchar());
for(in_n=0;in_c>='0'&&in_c<='9';in_c=getchar()) in_n=in_n*10+in_c-'0';
}
const int maxn=100005;
struct edge{
int to,next,l;
}e[maxn*2],e_[maxn*2];
int head[maxn],np,head_[maxn],np_;
void adde(int u,int v,int l){
e[++np]=(edge){v,head[u],l};
head[u]=np;
}
void adde_(int u,int v,int l){
e_[++np_]=(edge){v,head_[u],l};
head_[u]=np_;
}
int T,n,k,m,P,f[maxn][51],d[maxn];
bool vis[maxn],vising[maxn][51],err;
void dijstra(){//n->1反向搜尋
priority_queue<pair<int,int> >pq;
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
pq.push(make_pair(0,n)),d[n]=0;
int i,j,c;
while(!pq.empty()){
i=pq.top().second,pq.pop();
vis[i]=true;
for(int p=head_[i];p;p=e_[p].next){
j=e_[p].to,c=e_[p].l;
if(d[i]+c<d[j]){
d[j]=d[i]+c;
if(!vis[j]) pq.push(make_pair(-d[j],j));
}
}
}
}
int dp(int i,int K){
if(f[i][K]) return f[i][K];
if(vising[i][K]) err=true;
if(err) return 0;
vising[i][K]=true;
if(i==n) f[i][K]=1;//動態賦值
for(int p=head[i];p;p=e[p].next){
int j=e[p].to,c=e[p].l;
if(K>=d[j]+c-d[i]){
int tmp=dp(j,K-d[j]-c+d[i]);
f[i][K]=(f[i][K]+tmp)%P;
}
}
vising[i][K]=false;//修改結束
return f[i][K];
}
void build(){
int u,v,l;
np=np_=0,err=false;
memset(e,0,sizeof(e));
memset(f,0,sizeof(f));
memset(head,0,sizeof(head));
memset(vising,0,sizeof(vising));
memset(e_,0,sizeof(e_));
memset(head_,0,sizeof(head_));
scanf("%d%d%d%d",&n,&m,&k,&P);
for(int i=1;i<=m;i++)
scan(u),scan(v),scan(l),
adde(u,v,l),adde_(v,u,l);
}
void solve(){
dijstra();
dp(1,k);
printf("%d\n",err?-1:f[1][k]);
}
int main(){
scanf("%d",&T);
while(T--) build(),solve();
return 0;
}