[Luogu P6822PA2012]Tax] (http://www.luogu.com.cn/problem/P6822")
All right. Let's go!
- 题目描述
给出一个 n 个点 m 条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点 1 到点 n 的最小代价。起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边权。
这感觉非常棘手啊,这道题弯很多,首先不考虑连边建图,这道题该如何下手。
猜一猜:最短路算法是肯定的
可是在图论中通常是“ 停在点,经过边 ”,可这里是“ 经过点 ! ”,有没有一种反过来的感觉。
我们就用到了一种(类物理)的转换法
边点颠倒: 将边化为点,点化为边(连边时直接连存下来的边的编号即可)
但是直接这么连还是会超时,所以考虑连边建图
把无向边拆解为两条有向边
对于每个点,有几条进入它的入边,同样也有几条它延出的出边。我们按他们的边权从小到大排序
1.每条入边连相同所至点出边
2.而出边中,从前到后,E[i]->E[i+1] 权值为 E[i+1]-E[i] 反过来E[i+1]->E[i]权值为0
这里排序后,利用了差分 的思想
代码有点繁杂:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=5e6+5;
typedef long long ll;
ll dis[N],Len[N];
int n,m,Tot,cnt,Nxt[N],To[N],Head[N];
bool mark[N];
struct node {
int num;ll w;
bool operator<(const node u) const{
return w<u.w;
}
};
vector<node> G[100005];
void add_edge(int u,int v,ll w) {
Tot++; Nxt[Tot]=Head[u]; Len[Tot]=w; To[Tot]=v; Head[u]=Tot;
}
struct Node {
int p;ll w;
bool operator<(const Node u) const{
return w>u.w;
}
};
priority_queue<Node> Q;
void DJ(int s) {
dis[s]=0;
Q.push((Node){s,0});
while(!Q.empty()) {
int u=Q.top().p; Q.pop();
if(mark[u]) continue;
mark[u]=true;
for(int i=Head[u];i;i=Nxt[i]) {
int v=To[i];
if(!mark[v]&&dis[v]>dis[u]+Len[i]) {
dis[v]=dis[u]+Len[i];
Q.push((Node){v,dis[v]});
}
}
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) {
int u,v; ll w;
scanf("%d%d%lld",&u,&v,&w);
G[u].push_back((node){i*2-1,w});
G[v].push_back((node){i*2,w}); //相邻的一条互为反向边
}
for(int i=1;i<=n;i++) {
sort(G[i].begin(),G[i].end());
}
for(int sz=G[1].size(),j=0;j<sz;j++) {
int v2=G[1][j].num,v1;
if(v2&1) v1=v2+1; else v1=v2-1;
add_edge(0,v2,G[1][j].w);
}
for(int i=2;i<n;i++) {
for(int sz=G[i].size(),j=0;j<sz;j++) {
int v2=G[i][j].num,v1; ll w=G[i][j].w;
if(v2&1) v1=v2+1; else v1=v2-1;
add_edge(v1,v2,w);
if(j) {
add_edge(G[i][j-1].num,v2,w-G[i][j-1].w);
add_edge(v2,G[i][j-1].num,0);
}
}
}
for(int sz=G[n].size(),j=0;j<sz;j++) {
int v2=G[n][j].num,v1;
if(v2&1) v1=v2+1; else v1=v2-1;
add_edge(v1,m*2+1,G[n][j].w);
// printf("%d %d %d\n",v.num,m*2+1,v.w);
}
memset(dis,0x3f,sizeof(dis));
DJ(0);
printf("%lld",dis[m*2+1]);
return 0;
}