题意简述:每个溶洞和其他某些溶洞有喑道相连,两个溶洞之间可能有多条道路,也有可能没有,但没有一条暗道直接从自己连到自己。参赛者需要统一从一个大溶洞出发,并再次回到这个大溶洞。不能经过同一条暗道两次(起点编号为
1
)
对于
如果一条回路上有重复走暗道,那么和起点相连的重复暗道必然存在,因为如果离起点最近的重复暗道上的点是X,那么去掉X到X的回路,那么这条回路会变短。所以只要控制走出
1
和回到
比赛结束发现有3种以上姿势可以A掉这道题。
法一:暴力加个优化,当遇到
dis>nowans
就break掉。
法二:随机。一开始我以为是随机出边,但这样每次只有
1m
的概率成功,但他们高妙的随机是随机每个和起点相邻的边是入还是出,那么每次有
14
的概率成功。
法三:SPFA求起点到每个点的最短路和次短路,并且最短路和次短路从起点出发的边不一样,那么只要枚举一下回起点的边即可。
法三有绝对的正确性保证,但法一法二也很优秀啊。
法一:
#include<bits/stdc++.h>
const int N = 1e4 + 10;
const int M = 4e5 + 50;
const int INF = 1e9;
template <typename T> void read(T &x){
x = 0; char c = getchar();
for (; !isdigit(c); c = getchar());
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
}
int n,m,x,y,u,v,s,cnt,ans,first[N],dis[N];
struct edge{
int y,v,next;
bool flag;
}mp[M];
void ins(int x, int y, int v, int _v){
mp[++s] = (edge) {y,v,first[x],1}; first[x] = s;
mp[++s] = (edge) {x,_v,first[y],1}; first[y] = s;
}
struct rec{
int x,dis;
}h[M];
void push(rec x){
h[++cnt] = x;
int i = cnt;
while (i > 1 && h[i].dis < h[i >> 1].dis)
std::swap(h[i],h[i >> 1]),
i >>= 1;
}
rec poop(){
rec ret = h[1];
h[1] = h[cnt--];
int i = 1;
while (i*2<=cnt && h[i*2].dis<h[1].dis || i*2+1<=cnt && h[i*2+1].dis<h[1].dis){
int e = i * 2 + (i*2+1<=cnt && h[i*2+1].dis<h[i*2].dis);
std::swap(h[i],h[e]);
i = e;
}
return ret;
}
void Dijkstra(int x, int walked){
for (int i=1; i<=n; i++) dis[i] = INF;
dis[x] = walked;
push((rec){x,dis[x]});
while (cnt){
rec p = poop();
while (cnt && dis[p.x] != p.dis) p = poop();
if (dis[p.x] != p.dis) continue;
for (int t=first[p.x]; t; t=mp[t].next)
if (mp[t].flag && dis[p.x] + mp[t].v < dis[mp[t].y] && dis[p.x] + mp[t].v < ans)
dis[mp[t].y] = dis[p.x] + mp[t].v,
push((rec){mp[t].y,dis[mp[t].y]});
}
ans = std::min(ans,dis[1]);
}
int main(){
read(n);read(m);
s = 1;
while (m--)
read(x),read(y),read(u),read(v),
ins(x,y,u,v);
ans = INF;
for (int t=first[1]; t; t=mp[t].next)
mp[t^1].flag = 0,
Dijkstra(mp[t].y,mp[t].v),
mp[t^1].flag = 1;
printf("%d\n",ans);
return 0;
}
法二:
#include<bits/stdc++.h>
const int N = 1e4 + 10;
const int M = 4e5 + 50;
const int INF = 1e9;
template <typename T> void read(T &x){
x = 0; char c = getchar();
for (; !isdigit(c); c = getchar());
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
}
int n,m,x,y,u,v,s,cnt,ans,first[N],dis[N],ty[M];
struct edge{
int y,v,next;
bool flag;
}mp[M];
void ins(int x, int y, int v, int _v){
mp[++s] = (edge) {y,v,first[x],1}; first[x] = s;
mp[++s] = (edge) {x,_v,first[y],1}; first[y] = s;
}
struct rec{
int x,dis;
}h[M];
void push(rec x){
h[++cnt] = x;
int i = cnt;
while (i > 1 && h[i].dis < h[i >> 1].dis)
std::swap(h[i],h[i >> 1]),
i >>= 1;
}
rec poop(){
rec ret = h[1];
h[1] = h[cnt--];
int i = 1;
while (i*2<=cnt && h[i*2].dis<h[1].dis || i*2+1<=cnt && h[i*2+1].dis<h[1].dis){
int e = i * 2 + (i*2+1<=cnt && h[i*2+1].dis<h[i*2].dis);
std::swap(h[i],h[e]);
i = e;
}
return ret;
}
void build() {
for (int t=first[1]; t; t=mp[t].next)
if (rand() % 2)
mp[t].flag = 0,
mp[t^1].flag = 1;
else
mp[t].flag = 1,
mp[t^1].flag = 0;
}
void Dijkstra(){
for (int i=1; i<=n; i++) dis[i] = INF;
for (int t=first[1]; t; t=mp[t].next)
if (mp[t].flag)
dis[mp[t].y] = std::min(dis[mp[t].y], mp[t].v),
push((rec){mp[t].y, dis[mp[t].y]});
while (cnt){
rec p = poop();
while (cnt && dis[p.x] != p.dis) p = poop();
if (dis[p.x] != p.dis) continue;
for (int t=first[p.x]; t; t=mp[t].next)
if (mp[t].flag && dis[p.x] + mp[t].v < dis[mp[t].y])
dis[mp[t].y] = dis[p.x] + mp[t].v,
push((rec){mp[t].y,dis[mp[t].y]});
}
ans = std::min(ans,dis[1]);
}
int main(){
srand(time(0));
read(n);read(m);
s = 1;
for (int i=1; i <= m; i++)
read(x),read(y),read(u),read(v),
ins(x,y,u,v);
ans = INF;
for (int t=first[1]; t; t=mp[t].next)
ty[++ty[0]] = t;
for (int i=1; i <= 4 + 16 * (n <= 2000); i++)
build(),
Dijkstra();
printf("%d\n",ans);
return 0;
}
法三:
#include<bits/stdc++.h>
const int INF = 1e9;
const int N = 1e4 + 10;
const int M = 2e5 + 10;
template <typename T> void read(T &x) {
x = 0; char c = getchar();
for (; !isdigit(c); c = getchar());
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
}
struct edge {
int y, v, next;
}mp[M*2];
int n, m, s, first[N], dis[N][2], from[N][2], q[N + 10];
bool inq[N];
void ins(int x, int y, int v, int _v) {
mp[++s] = (edge) {y, v, first[x]}; first[x] = s;
mp[++s] = (edge) {x, _v, first[y]}; first[y] = s;
}
void SPFA() {
for (int i=1; i <= n; i++)
dis[i][0] = dis[i][1] = INF;
int head = 1, tail = 1;
for (int t=first[1]; t; t=mp[t].next)
if (mp[t].v < dis[mp[t].y][0]) {
dis[mp[t].y][0] = mp[t].v;
from[mp[t].y][0] = t;
if (!inq[mp[t].y])
inq[q[tail++] = mp[t].y] = 1;
}
while (head != tail) {
int x = q[head];
for (int t=first[x]; t; t=mp[t].next) {
int y = mp[t].y;
if (dis[x][0] + mp[t].v < dis[y][0]) {
dis[y][1] = dis[y][0];
from[y][1] = from[y][0];
dis[y][0] = dis[x][0] + mp[t].v;
from[y][0] = from[x][0];
if (!inq[mp[t].y]) {
inq[q[tail++] = mp[t].y] = 1;
if (tail > N) tail = 1;
}
continue;
}
if (dis[x][0] + mp[t].v < dis[y][1] && from[y][0] != from[x][0]) {
dis[y][1] = dis[x][0] + mp[t].v;
from[y][1] = from[x][0];
if (!inq[mp[t].y]) {
inq[q[tail++] = mp[t].y] = 1;
if (tail > N) tail = 1;
}
}
}
inq[q[head++]] = 0;
if (head > N) head = 1;
}
}
int main() {
read(n);read(m);
s = 1;
int x, y, v, _v;
for (int i=1; i <= m; i++)
read(x), read(y), read(v), read(_v),
ins(x, y, v, _v);
SPFA();
int ans = INF;
for (int t=first[1]; t; t=mp[t].next)
ans = std::min(ans, dis[mp[t].y][from[mp[t].y][0] == t] + mp[t^1].v);
printf("%d\n",ans);
return 0;
}