最短路(建图)
虫洞
题目描述
输入格式
输出格式
样例
数据范围与提示
对于30%的数据: 1<=N<=100,1<=M<=500
对于60%的数据: 1<=N<=1000,1<=M<=5000
对于100%的数据: 1<=N<=5000,1<=M<=30000
其中20%的数据为1<=N<=3000的链
1<=u,v<=N, 1<=k,w[i],s[i]<=200
思路:分层图,将白洞作为一层(结点1-n),黑洞作为一层(节点n+1-2*n)
第一种:u与v颜色相同,因为走一条边花费一个单位时间,所以从u走到v时v以变色,Insert(u(u为白点),v+n(v为黑点),k(边权))(若开始u为白v为白)反之同上
第二种:u与v颜色不同,因为点会按时换颜色所以即使u开始为黑v开始为白一定得建一条u到v(u到v后v变成白点)的边,但u可变成黑,同时v变白,再建一条u+n到v的边,反之,同上
第三种:每一点可在原地停留,所以建u到u+n和u+n到u的边
之后跑最短路,注意起始位置与1的颜色有关,最后注意比较两种情况
#include<cstdio> #include<queue> #include<cstring> #include<algorithm> using namespace std; const int maxn=10000+10,maxm=30000+10,inf=0x3f3f3f3f; int color[maxn],w[maxn],s[maxn]; struct Edge{ int to,next,w; }e[maxm<<2]; struct Node{ int num,w; Node(){}; Node(int x,int y){ num=x; w=y; } bool operator <(const Node &a)const{ return w>a.w; } }; int head[maxm<<2],tot=0; void Insert(int a,int b,int c){ e[++tot].to=b; e[tot].w=c; e[tot].next=head[a]; head[a]=tot; } priority_queue<Node> q; int d[maxn]; void Dij(int x){ bool vis[maxn]; memset(vis,0,sizeof(vis)); memset(d,0x3f,sizeof(d)); d[x]=0; q.push(Node(x,0)); while(!q.empty()){ Node t=q.top(); int u=t.num; q.pop(); if(vis[u]) continue; vis[u]=1; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(d[v]>d[u]+e[i].w){ d[v]=d[u]+e[i].w; q.push(Node(v,d[v])); } } } } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&color[i]); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<=n;i++) scanf("%d",&s[i]); for(int i=1;i<=m;i++){ int u,v,k; scanf("%d%d%d",&u,&v,&k); if(color[u]==color[v]){ Insert(u,v+n,k); Insert(u+n,v,k);//当u与v颜色相同时,都得见两条边 } else{ Insert(u,v,max(0,k-abs(w[u]-w[v]))); Insert(u+n,v+n,k+abs(w[u]-w[v]));//u与v颜色不同 } } for(int i=1;i<=n;i++){ Insert(i,i+n,0); Insert(i+n,i,s[i]); //在此处停留 } if(color[1]==0) Dij(1); else Dij(1+n);//注意起始位置 printf("%d\n",min(d[n],d[2*n]));//最后要比较两种可能结果 return 0; }