伊吹萃香
2920 – 【模拟试题】伊吹萃香
Description
在幻想乡,伊吹萃香是能够控制物体密度的鬼王。因为能够控制密度,所以萃香能够制造白洞和黑洞,并可以随时 改变它们。某一天萃香闲着无聊,在妖怪之山上设置了一些白洞或黑洞,由于引力的影响,给妖怪们带来了很大的麻烦。于是他们决定找出一条消耗体力最少的
路,来方便进出。已知妖怪之山上有 N个路口(编号 1…N),每个路口都被萃香设置了一定质量白洞或者黑洞。原本在各个路口之间有 M 条单向路,走过每一条路需要消耗一定量的体力以及 1 个单位的时间。由于白洞和黑洞的存在,走过每条路需要消耗的体力也就产生了变化,假设一条道路两端路口黑白洞的质量差为delta:
- 从有白洞的路口走向有黑洞的路口,消耗的体力值减少 delta,若该条路径消耗的体力值变为负数的话,取为0。
- 从有黑洞的路口走向有白洞的路口,消耗的体力值增加 delta。
- 如果路口两端均为白洞或黑洞,消耗的体力值无变化。
由于光是放置黑洞白洞不足以体现萃香的强大,所以她决定每过 1 个单位时间,就把所有路口的白洞改成黑洞,黑洞改成白洞。当然在走的过程中你可以选择在一个路口上停留 1 个单位的时间,如果当前路口为白洞,则不消耗体力,否则消耗 s[i]的体力。现在请你计算从路口1 走到路口N 最小的体力消耗。保证一定存在道路从路口 1到路口N。
Input
第1行:2个正整数N, M
第2行:N个整数,第 i个数为0 表示第i个路口开始时为白洞,1 表示黑洞
第3行:N个整数,第 i个数表示第i个路口设置的白洞或黑洞的质量w[i]
第4行:N个整数,第 i个数表示在第 i个路口停留消耗的体力 s[i]
第5…M+4行:每行3 个整数,u, v, k,表示在没有影响的情况下,从路口u走到路口 v需要消
耗k的体力。
Output
第1行:1个整数,表示消耗的最小体力
Sample Input
4 5
1 0 1 0
10 10 100 10
5 20 15 10
1 2 30
2 3 40
1 3 20
1 4 200
3 4 200
Sample Output
130
Hint
样例说明:按照1 -> 3 -> 4的路线。
对于30%的数据:1 <= N <= 100, 1 <= M <= 500
对于60%的数据:1 <= N <= 1,000, 1 <= M <= 5,000
对于100%的数据:1 <= N <= 5,000, 1 <= M <= 30,000
其中20%的数据为1 <= N <= 3000的链
1 <= u,v <= N, 1 <= k,w[i],s[i] <= 200
Source
幻想乡
本题实际上是简化的 分层图最短路,
可将原图中的点集拆为颜色相反的两部分,分别代表奇时刻和偶时刻,不同时刻之间再相互连边,最后使用最短路算法解决即可。
时间复杂度:O(N^2)/O(NlogN)/O(KM);期望得分:100
每个点i,2i表示第i个洞的白色形态(可以这么理解吧),2i-1表示第i个洞的黑色形态
#include<iOStream>
#include<queue>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=10005,maxm=30005,INF=0x7fffffff/2;
struct Edge{int to,next,val;}a[maxm*4];
int n,m,h[maxm],cnt=0,vst[maxn],d[maxn],hole[maxn],w[maxn],tl[maxn];
queue<int>q;
void AddEdge(int x,int y,int z)
{
a[++cnt].to=y;a[cnt].next=h[x];a[cnt].val=z;h[x]=cnt;
}
void SPFA(int v0)//最短路STL队列
{
int i;
for(i=1;i<=2*n;i++)d[i]=INF;d[v0]=0;q.push(v0);vst[v0]=1;
while(!q.empty())
{
int x=q.front();q.pop();vst[x]=0;
for(i=h[x];i;i=a[i].next)
{
int y=a[i].to;
if(d[y]>d[x]+a[i].val)
{
d[y]=d[x]+a[i].val;
if(!vst[y]){q.push(y);vst[y]=1;}
}
}
}
}
int main()
{
int j,i,x,y,z;
cin>>n>>m;
for(i=1;i<=n;i++)cin>>hole[i];//hole表示起初这个洞是black还是white
for(i=1;i<=n;i++)cin>>w[i];//质量
for(i=1;i<=n;i++)cin>>tl[i];//原地螺旋(划掉)走需要的体力
for(i=1;i<=m;i++)
{
cin>>x>>y>>z;
if(hole[x]==hole[y])//x,y都为同颜色
{
AddEdge(2*x,2*y-1,z);//x白到y黑
AddEdge(2*x-1,2*y,z);}//x黑到y白
else
{
int delta=abs(w[x]-w[y]);
AddEdge(2*x-1,2*y-1,z+delta);//x白到y白
AddEdge(2*x,2*y,max(0,z-delta));//x黑到y黑
}
}
for(i=1;i<=n;i++)//2*i表示白,2*i-1表示黑
{
AddEdge(i*2-1,i*2,tl[i]);//i黑到i白,自我螺旋到黑,消耗体力
AddEdge(i*2,i*2-1,0);//i白到i黑 ,不用花费体力
}
SPFA(2-hole[1]);//注意不是从0开始
cout<<min(d[2*n],d[n*2-1]);
return 0;
}