您的位置:首页 > 其它

hdu 6071 Lazy Running(spfa+同余最短路)

2017-08-10 11:01 337 查看

Lazy Running

[b]Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)

Total Submission(s): 1186    Accepted Submission(s): 496
[/b]

[align=left]Problem Description[/align]
In HDU, you have to run along the campus for 24 times, or you will fail in PE. According to the rule, you must keep your speed, and your running distance should not be less thanK
meters.

There are 4
checkpoints in the campus, indexed as p1,p2,p3
and p4.
Every time you pass a checkpoint, you should swipe your card, then the distance between this checkpoint and the last checkpoint you passed will be added to your total distance.

The system regards these 4
checkpoints as a circle. When you are at checkpoint pi,
you can just run to pi−1
or pi+1(p1
is also next to p4).
You can run more distance between two adjacent checkpoints, but only the distance saved at the system will be counted.



Checkpoint p2
is the nearest to the dormitory, Little Q always starts and ends running at this checkpoint. Please write a program to help Little Q find the shortest path whose total distance is not less thanK.
 

[align=left]Input[/align]
The first line of the input contains an integer
T(1≤T≤15),
denoting the number of test cases.

In each test case, there are 5
integers K,d1,2,d2,3,d3,4,d4,1(1≤K≤1018,1≤d≤30000),
denoting the required distance and the distance between every two adjacent checkpoints.
 

[align=left]Output[/align]
For each test case, print a single line containing an integer, denoting the minimum distance.
 

Sample Input
1

2000 600 650 535 380

Sample Output
2165

Hint

The best path is 2-1-4-3-2.

题意:
一个环1->2->3->4->1 人在任意一个点都可以走向附近的的两个点,人从2出发,最后回到2,问求出>=k的最小的人走的路程

解析:
取w=\min(d_{1,2},d_{2,3})w=min(d​1,2​​,d​2,3​​),那么对于每一种方案,均可以通过往返跑ww这条边使得距离增加2w2w。也就是说,如果存在距离为kk的方案,那么必然存在距离为k+2wk+2w的方案。

设dis_{i,j}dis​i,j​​表示从起点出发到达ii,距离模2w2w为jj时的最短路,那么根据dis_{2,j}dis​2,j​​解不等式即可得到最优路线。

时间复杂度O(w\log
w)O(wlogw)。
也就是说,假设我们将任意一条长度大于k的回路(从2出发回到2)称为可行路径,那么任意一条可行路径加上2w一定还是一条可行路径,所有可行路径中,最短的是k,最长的为 k + 2 * n * w,(n趋近正无穷),显然我们不可能求出所有的可行路径来,由于所有可行路径长度中都含有2w(有可能含0个),我们可以考虑按对2w的余数分块,这样我们只要求出%2w == 0的最短可行路径,%2w
== 1的最短可行路径。。。一直到%2w == 2w - 1的最短可行路径,然后在这2w条可行路径中找一条最短的就是答案了(因为这条一定是所有可行路径中最短的那条)

这里用同余最短路的原因是在对%2w余数为j的数,互相都可以通过加减2w来得到,所以只需要找到%2w=j这一块中的最小的那个数,就可以得到解了

然后问题就是如何求出%2w同余的所有可行路径中最短的那条了,用dis[i][j]表示从2号点出发到达i,长度%2w为j的最短路径的长度(注意这里不一定是可行路径,长度可以小于K,因为我们可以最后再添几个2w使其刚好大于K,并且添加2w以后并不会改变其长度%2w
== j的性质),这个数组可以用dijkstra的思想求出来,注意要加堆优化。
参考:http://blog.csdn.net/lxy767087094/article/details/76723787

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define INF 6446744073709551616
using namespace std;
typedef long long int ll;

typedef struct node
{
int d;
int mm;
}node;

int d[5],w;
ll dist[5][60010],k;
int visit[5][60010];
ll map[5][5];

ll increase(ll res)  //对小于K的路径进行扩充使得变成%w余数不变且大于k的满足以上条件最小的数
{
ll rr=res;
if(res<k)
{
if((k-res)%w) rr=res+((k-res)/w+1)*w;
else rr=res+((k-res)/w)*w;
}
return rr;
}

void spfa(node s)
{
memset(visit,0,sizeof(visit));
for(int i=1;i<=4;i++)
{
for(int j=0;j<w;j++)
{
dist[i][j]=INF;
}
}
queue<node> mq;

mq.push(s);
visit[s.d][s.mm]=1;
dist[s.d][s.mm]=0;
while(mq.size())
{
node tmp=mq.front();
mq.pop();

for(int i=1;i<=4;i++)
{
if(map[tmp.d][i]==INF) continue;  //若两个点间没有路则continue;
ll res1=dist[tmp.d][tmp.mm]+map[tmp.d][i];  //2->当前该点tmp.d的最短路+tmp.d->i的最短路
ll res2=dist[i][res1%w];       //2->i的与res1同余的最短路
ll rr1,rr2;
rr1=increase(res1);   //对他们进行最小扩充
rr2=increase(res2);

if(rr2>rr1)
{

dist[i][res1%w]=res1;
if(visit[i][res1%w]==0)
{
node p;
p.d=i;
p.mm=res1%w;
mq.push(p);
visit[p.d][p.mm]=1;
}
}

}
visit[tmp.d][tmp.mm]=0;
}
}

int main()
{
int t;

scanf("%d",&t);
while(t--)
{
scanf("%lld",&k);
for(int i=1;i<=4;i++)
scanf("%d",&d[i]);
w=min(d[1],d[2]);
w=w*2;
for(int i=1;i<=4;i++)
{
for(int j=1;j<=4;j++)
{
map[i][j]=INF;
}
}
map[1][2]=map[2][1]=d[1];
map[2][3]=map[3][2]=d[2];
map[3][4]=map[4][3]=d[3];
map[4][1]=map[1][4]=d[4];
node s;
s.d=2;
s.mm=0;
spfa(s);

ll Min=INF;
for(int i=0;i<w;i++)  //在2->2的不同余数的路中找最短路
{
ll tmp=increase(dist[2][i]);
if(tmp<Min)
Min=tmp;
}
printf("%lld\n",Min);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息