[POI 2003] Smugglers 解题记录
题意简述
有
n
n
n 种金属(金子编号为
1
1
1),每种金属有自己的单价,金属之间可以互相转换,编号为
i
i
i 的金属转换为
j
j
j 需要
k
k
k 的代价。穿过边境花费
50
%
50\%
50% 当前的金属的单价。
问要把金子带过边境,需要的最少的代价。
题目分析
对于题目中的“
i
i
i 到
j
j
j 花费
k
k
k”,很容易就可以想到建图。考虑如何建图。
可以把图分为两部分:过边境前和过边境后。
那么我们就可以设
1
∼
n
1 \sim n
1∼n 为过边境前的部分,
n
+
1
∼
2
n
n+1 \sim 2n
n+1∼2n 为过边境后的部分。对于金属
i
i
i 和金属
j
j
j,如果可以转换,那么就把
i
i
i 和
j
j
j 之间连一条单向边,同时在
i
+
n
i+n
i+n 和
j
+
n
j+n
j+n 之间也连一条单向边。
对于两个部分之间的连接,其实就是扣税的过程,直接将
i
i
i 与
i
+
n
i+n
i+n 连一条权值为金属
i
i
i 单价的
50
%
50\%
50% 的边。
最后跑一遍最短路即可。
AC Code
#include<bits/stdc++.h>
#define arrout(a,n) rep(i,1,n)std::cout<<a[i]<<" "
#define arrin(a,n) rep(i,1,n)std::cin>>a[i]
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define dep(i,x,n) for(int i=x;i>=n;i--)
#define erg(i,x) for(int i=head[x];i;i=e[i].nex)
#define dbg(x) std::cout<<#x<<":"<<x<<" "
#define mem(a,x) memset(a,x,sizeof a)
#define all(x) x.begin(),x.end()
#define arrall(a,n) a+1,a+1+n
#define PII std::pair<int,int>
#define m_p std::make_pair
#define u_b upper_bound
#define l_b lower_bound
#define p_b push_back
#define CD const double
#define CI const int
#define int long long
#define il inline
#define ss second
#define ff first
#define itn int
CI N=1e5+5;
struct edge {
int to,nex,data;
}e[N<<2];//图要开4倍
int n,m,tot,head[N],dis[N];
void add(int x,int y,int z) {
e[++tot].to=y;
e[tot].data=z;
e[tot].nex=head[x];
head[x]=tot;
}
struct node {
int to,data;
bool operator<(const node& b)const {
return data>b.data;
}
};
std::priority_queue<node> q;
void dijkstra(int st) {
mem(dis,0x3f);
q.push(node{st,0});
dis[st]=0;
while(!q.empty()) {
node u=q.top();
q.pop();
int x=u.to;
if(dis[x]!=u.data) {
continue;
}
erg(i,x) {
int y=e[i].to,z=e[i].data;
if(dis[y]>dis[x]+z) {
dis[y]=dis[x]+z;
q.push(node{y,dis[y]});
}
}
}
}
signed main() {
std::cin>>n;
rep(i,1,n) {
int x;
std::cin>>x;
add(i,n+i,x/2);//扣税
}
std::cin>>m;
rep(i,1,m) {
int x,y,z;
std::cin>>x>>y>>z;
add(x,y,z);//转换
add(x+n,y+n,z);
}
dijkstra(1);
std::cout<<dis[n+1];
return 0;
}