題面:
Fxx and game
Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 2264 Accepted Submission(s): 600
Problem Description
Young theoretical computer scientist Fxx designed a game for his students.
In each game, you will get three integers X,k,t.In each step, you can only do one of the following moves:
1.X=X−i(0<=i<=t).
2.if k|X,X=X/k.
Now Fxx wants you to tell him the minimum steps to make X become 1.
Input
In the first line, there is an integer T(1≤T≤20) indicating the number of test cases.
As for the following T lines, each line contains three integers X,k,t(0≤t≤106,1≤X,k≤106)
For each text case,we assure that it’s possible to make X become 1。
Output
For each test case, output the answer.
Sample Input
2
9 2 1
11 3 3
Sample Output
4
3
分析:
此題可用dp求解
分解子問題:我們設 表示i變爲1需要的方法數
因爲dp[i]可由 , 轉移過來
我們可以寫出狀態轉移方程:
邊界條件:dp[1]=0
時間複雜度:
但是該方法時間複雜度過高,必須要優化
單調隊列的條件是:首先隊列的值保持單調,維護答案一直在隊首,然後隊列裏面的時間表現單調性,還有就是彈掉的東西一定沒有新入隊的優
維護一個dp[i]從隊尾到隊頭遞增的隊列
因此我們每次算好dp[i]的時候把隊尾中dp值小於等於dp[i]的都出隊(隊列裏面的都是下標比i大的,值又沒i優,是無用的)
最後在隊頭彈出不滿足條件的值
代碼:
#include<iostream>
#include<cstring>
#define maxn 1000020
using namespace std;
int c,x,k,t;
int dp[maxn],q[maxn];
int head,tail;
int main() {
cin>>c;
while(c--) {
cin>>x>>k>>t;
int ans=0;
if(t==0) { //特判
while(x!=1) {
x/=k;
ans++;
}
}else if(k==0) {
if((x-1)%t==0) ans=(x-1)/t;
else ans=(x-1)/t+1;
} else {
head=0,tail=1;
memset(q,0,sizeof(q));
memset(dp,0x7f,sizeof(dp));
dp[1]=0;
q[head]=1;
for(int i=2; i<=x; i++) {
while(i-q[head]>t&&head<tail) head++;//單調隊列優化
dp[i]=dp[q[head]]+1;
if(i%k==0) dp[i]=min(dp[i],dp[i/k]+1);
while(dp[q[tail]]>dp[i]&&head<tail) tail--;
q[++tail]=i;
}
ans=dp[x];
}
cout<<ans<<endl;
}
}