传送门:HDU5945
题意:给定3个数x,k,t,和两种操作
1) x=x-i;0<=i<=t;
2) if x|k x=x/k;
问最少经过多少步能将x变为1,输入保证x能变为1.
比赛的时候看这题的想法是广搜,但是第一次交MLE,一顿xjb改以后又交了一次,竟然过了,就没再多想,结果终测还是MLE。。后来看题解大多数都是用dp+单调队列做的,但是bfs也不是不可以,不过要加一个标记数组。把比赛时的代码拿来加上标记数组改了改,怎么交怎么RE,又去看题解发现题解是从x往1搜,而我是从1往x搜,又改一发才终于AC。
下面是BFS代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include<math.h>
#define inf 0x3f3f3f3f
#define M 1000005
using namespace std;
int x,k,t;
int book[M];
int ans=inf;
struct node{
int num,t;
};
int bfs()
{
if(k==1&&t)
return (x-1+t-1)/t;
memset(book,0,sizeof(book));
queue<node>q;
node a;
a.num=x;
a.t=0;
q.push(a);
book[x]=1;
while(!q.empty())
{
node b=q.front();
q.pop();
node c;
if(b.num==1)
return b.t;
if(2<=b.num&&b.num<=(t+1)||b.num==k)//这里有个BUG不知道为啥,就是把b.num==k注释掉以后再交是WA
return b.t+1;//但是我觉得顶多会多运行几次啊,这条语句应该是剪枝啊。。不明白把剪枝去掉以后怎么就WA了。。
if(b.n
{
c.num=b.num/k;
c.t=b.t+1;
q.push(c);
book[b.num/k]=1;
}
//int m=min(t,b.num-1);
for(int i=t;i>=1;i--)
{
if(!book[b.num-i])
{
c.num=b.num-i;
c.t=b.t+1;
q.push(c);
book[b.num-i]=1;
}
else
break;
}
}
}
int main()
{
int a;
scanf("%d",&a);
while(a--)
{
scanf("%d%d%d",&x,&k,&t);
printf("%d\n",bfs());
}
return 0;
}
下面来说单调队列,这个就有点强了,以前只是接触过单调栈,这次又涨了新知识。
不瞎扯,下面是我觉得挺好理解的单调队列的学习博客:
下面是我觉得解释的比较明白的两篇题解。。
再下面就是我自己的代码了。。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct node{
int num,id;
}q[1000005];
int dp[1000005];
int main()
{
int x,k,t;
int cnt;
scanf("%d",&cnt);
while(cnt--)
{
memset(dp,127,sizeof(dp));
//memset(q,0,sizeof(q));
scanf("%d%d%d",&x,&k,&t);
int head=1,tail=1;
dp[1]=0;
q[tail].num=dp[1];
q[tail].id=1;
for(int i=2;i<=x;i++)
{
while(tail>head&&q[head].id<max(1,i-t))
head++;
dp[i]=min(dp[i],q[head].num+1);
if(i%k==0)dp[i]=min(dp[i],dp[i/k]+1);
while(tail>=head&&dp[i]<=q[tail].num)
tail--;
tail++;
q[tail].num=dp[i];
q[tail].id=i;
}
printf("%d\n",dp[x]);
}
return 0;
}
有趣的是bfs比单调队列快好多。。