E - Minimum Path (Educational Codeforces Round 102 (Rated for Div. 2))
题意:给你一张无向图,无重边无自环。给出定义:对于一条路径(不一定是简单路径,比如对于图\(1-2-3,1->2->3->2\)这种有些边走了2次的就不是简单路径),他的路径总权值为路径上每一条经过边的和减去路径上最大的边权加上路径上最小的边权。让你求1点到每个点的最小满足上述条件的路径权值。
做法:由于不知道从1到某一点的路径究竟是怎么样的(不知道经过哪些点,不知道最大最小值,不知道是否为简单路径。。。)。但这条路径中只有一个最大值和一个最小值,对于某一条边我们可以得到4个状态:
①这条边即不是最大值也不是最小值。
②这条边是最大值
③这条边是最小值
④这条边即是最大值也是最小值
这样可以用类似dp的方法,通过将图分层,把所有可行状态枚举出来去更新每个点的答案。
代码有两种写法:
一:dis分层
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL)
const ll inf = 1e18 + 7;
const int maxn = 2e5 + 10;
struct Node {
ll dis;
int from, v1, v2;
friend bool operator <(Node x, Node y){
return x.dis > y.dis;
}
};
int head[maxn], edge_cnt = 0;
struct edge {
int to;
ll cost;
int next;
}e[maxn << 1];
void add(int from, int to, ll cost)
{
e[++edge_cnt] = { to,cost,head[from] };
head[from] = edge_cnt;
}
ll dis[maxn][2][2];
int n;
void dij()
{
priority_queue<Node>q;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++)
dis[i][j][k] = inf;
q.push({ 0,1,0,0 });
while (!q.empty())
{
Node now = q.top();
q.pop();
int from = now.from; ll dist = now.dis;
bool v1 = now.v1, v2 = now.v2;
if (now.dis > dis[from][v1][v2])continue;
for (int i = head[from]; ~i; i = e[i].next)
{
int to = e[i].to; ll cost = e[i].cost;
if (dis[to][v1][v2] > dist + cost)
{
dis[to][v1][v2] = dist + cost;
q.push({ dis[to][v1][v2],to,v1,v2 });
}
if (!v1 && dis[to][1][v2] > dist)
{
dis[to][1][v2] = dist;
q.push({ dis[to][1][v2],to,1,v2 });
}
if (!v2 && dis[to][v1][1] > dist + 2 * cost)
{
dis[to][v1][1] = dist + 2 * cost;
q.push({ dis[to][v1][1],to,v1,1 });
}
if (!v1 && !v2 && dis[to][1][1] > dist + cost)
{
dis[to][1][1] = dist + cost;
q.push({ dis[to][1][1],to,1,1 });
}
}
}
for (int i = 2; i <= n; i++)
cout << dis[i][1][1] << " ";
}
int main()
{
//freopen("C:\\test.txt", "r", stdin);
fastio;
memset(head, -1, sizeof(head));
int m;
cin >> n >> m;
while (m--)
{
int x, y, z;
cin >> x >> y >> z;
add(x, y, z);
add(y, x, z);
}
dij();
return 0;
}
二:开4倍的点
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL)
const ll inf = 1e18 + 7;
const int maxn = 2e5 + 10;
struct Node {
ll dis;
int from;
friend bool operator <(Node x, Node y){
return x.dis > y.dis;
}
};
int head[4 * maxn], edge_cnt = 0;
struct edge {
int to;
ll cost;
int next;
}e[maxn * 18 + 1];
void add(int from, int to, ll cost)
{
e[++edge_cnt] = { to,cost,head[from] };
head[from] = edge_cnt;
}
ll dis[4*maxn];
int n;
void dij()
{
priority_queue<Node>q;
for (int i = 1; i <= 4 * n; i++)
dis[i] = inf;
dis[1] = 0;
q.push({ 0,1});
while (!q.empty())
{
Node now = q.top();
q.pop();
int from = now.from; ll dist = now.dis;
if (now.dis > dis[from])continue;
for (int i = head[from]; ~i; i = e[i].next)
{
int to = e[i].to; ll cost = e[i].cost;
if (dis[to] > dist + cost)
{
dis[to] = dist + cost;
q.push({ dis[to],to});
}
}
}
for (int i = 3 * n + 2; i <= 4 * n; i++)
cout << dis[i] << " ";
}
int main()
{
//freopen("C:\\test.txt", "r", stdin);
fastio;
memset(head, -1, sizeof(head));
int m;
cin >> n >> m;
while (m--)
{
int x, y, z;
cin >> x >> y >> z;
add(x, y, z);
add(y, x, z);
add(x + n, y + n, z);
add(y + n, x + n, z);
add(x + 2 * n, y + 2 * n, z);
add(y + 2 * n, x + 2 * n, z);
add(x + 3 * n, y + 3 * n, z);
add(y + 3 * n, x + 3 * n, z);
add(x, y + n, 0);
add(y, x + n, 0);
add(x, y + 2 * n, 2 * z);
add(y, x + 2 * n, 2 * z);
add(x, y + 3 * n, z);
add(y, x + 3 * n, z);
add(x + n, y + 3 * n, 2 * z);
add(y + n, x + 3 * n, 2 * z);
add(x + 2 * n, y + 3 * n, 0);
add(y + 2 * n, x + 3 * n, 0);
}
dij();
return 0;
}