傳送門
- 題目:
四個點1,2,3,4順序連成環,給定相鄰兩點間的距離,問從2號點出發再回到2號點的所有路徑中,路徑長度不小於k的最小值。輸出這個最小值。1≤k≤1e18
- 思路:
主要用到同餘的思想。
記w=min(dis12,dis23)對於2→2的一條路徑,記其路徑長度爲len,那麼len+k∗2w,(k≥0)對應的路徑也是一種合法路徑,這些合法路徑長度根據%2w的值將其分爲2w組,並且求出每個同餘類i對應的最短的合法路徑的長度mlen[i]。
mlen[i]表示從2→2的合法路徑中(路徑長度爲len),滿足len%(2w)=i的最小的len值。
對於每個同餘類i,如果mlen[i]≥k那麼更新答案,否則,在mlen[i]的基礎上累加2w,直至滿足長度≥k,再更新答案。
那麼怎麼獲得mlen[]數組呢,可以暴搜,也可以用spfa做。
用spfa做的話dis[i][j]表示從2出發到達i滿足%(2w)=j的最小長度。隊列裏存上一個點距離當前點的最短距離,初始化存入{2,0}。
- ac代碼:
#include <iostream>
#include <vector>
#include <limits.h>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 6e4+10;
struct Edge{
int nxt; ll val;
};
struct node{
int now; ll v;
};
vector<Edge> edge[5];
queue<node> q;
int t;
ll d12, d23, d34, d41, w, k, dis[5][maxn];
void spfa()
{
for(int i = 0; i <= 4; i++)
for(int j = 0; j < w; j++)
dis[i][j] = LLONG_MAX;
q.push({2, 0});
while(!q.empty())
{
int now = q.front().now; ll v = q.front().v; q.pop();
for(auto e: edge[now])
{
ll tmp = v+e.val;
if(dis[e.nxt][tmp%w]>tmp)
{
dis[e.nxt][tmp%w] = tmp;
q.push({e.nxt, tmp});
}
}
}
}
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%lld %lld %lld %lld %lld", &k, &d12, &d23, &d34, &d41);
w = 2*min(d12, d23);
edge[1].push_back({2, d12}); edge[2].push_back({1, d12});
edge[2].push_back({3, d23}); edge[3].push_back({2, d23});
edge[3].push_back({4, d34}); edge[4].push_back({3, d34});
edge[4].push_back({1, d41}); edge[1].push_back({4, d41});
spfa();
ll ans = LLONG_MAX;
for(int i = 0; i < w; i++)
{
if(dis[2][i]>=k) ans = min(ans, dis[2][i]);
else
{
ll sub = k-dis[2][i];
ll p = sub%w==0 ? sub : (sub/w+1)*w;
ans = min(ans, dis[2][i]+p);
}
}
printf("%lld\n", ans);
for(int i = 1; i <= 4; i++) edge[i].clear();
}
return 0;
}