33,007
社区成员
发帖
与我相关
我的任务
分享
//我称这个题为随时间t变化的动态广度搜索问题,本层扩展前必须将上层节点扩展完毕,这是与其它广搜题最大的区别
#include<iostream>
using namespace std;
const int MAX = 1000;
const int SIZE = 1000000;
const int DN = 0;
const int UP = 1;
int a[MAX][3]; //每根桩升降的时间间隔
int mark[100][MAX]; //标记t时刻某根桩上是否已经站了人,防止重复站人,大大提高效率
int state[MAX]; //记录时间t时各桩的升降状态
int front, rear;
struct Queue
{
int pos; //所在位置(即哪个桩上)
int time; //本次所处时间
}q[SIZE];
int min(int a, int b)
{
return(a < b ? a : b);
}
void Push(int t, int pos)
{
if(mark[t][pos]) return; //若当前时间下该状态已入队,则不再入队,避免重复扩展,大大减少了扩展次数
q[++rear].pos = pos;
q[rear].time = t;
mark[t][pos] = 1; //置标记为1,表示已入队
}
void main()
{
int n;
int cur;
int len = 1;
cin >> n;
while(len <= n)
{
cin >> a[len][0] >> a[len][1];
a[len][2] = a[len][0] + a[len][1]; //计算出每次完整时间周期,减少在队列中的重复运算
len ++;
}
front = rear = -1;
q[++rear].pos = 0;
q[rear].time = 0;
for(int t = 1; ; t ++)
{
while(q[front].time == t-1) //队列按时间t分层,在本层扩展时上层的必须扩展完毕,这样才能保证求到最短时间
{
cur = q[++front].pos; //出队,开始扩展节点
if(cur == len) //到达对岸便输出最短时间,并退出程序
{
cout << endl << q[front].time << endl;
exit(0);
}
state[0] = state[len] = UP; //左右两岸始终设为UP,便于处理
mark[t][cur] = 0; //始终可以站在原桩上,标记为0,可扩展
Push(t, cur);
for(int i = 1, mod = 0; i < len; i ++)
{
mod = t % a[i][2];
state[i] = (mod && mod <= a[i][0] ? UP : DN); //得到每个桩当前时间的状态
}
for(int j = cur+1; j <= min(cur+5,len); j ++)
{
if(state[j] == DN) break; //如果将上的桩是下降状态,则停止前进
Push(t, j);
} //不必再跑到(当前桩)前5个桩上去了,即使此时没有桩可以上也可以停在原桩上,而不必再后退到前面的桩上;
//退一步讲,即使前面5个某桩可以跳到原桩前面,那原桩必然也可以走到此处,因为这段肯定都是上升状态
//而且说不定还可以走得更远,所以无论如何都不需要往后退到前5个桩上,题目说能走到前面5个桩上实际是个干扰
}
}
}