分层图食用简介

N个虫洞,M条单向跃迁路径。从一个虫洞沿跃迁路径到另一个虫洞需要消耗一定量的燃料和1单位时间。虫洞有白洞和黑洞之分。设一条跃迁路径两端的虫洞质量差为delta。  
1.从白洞跃迁到黑洞,消耗的燃料值减少delta,若该条路径消耗的燃料值变为负数的话,取为0。  
2.从黑洞跃迁到白洞,消耗的燃料值增加delta。  
3.路径两端均为黑洞或白洞,消耗的燃料值不变化。  
作为压轴题,自然不会是如此简单的最短路问题,所以每过1单位时间黑洞变为白洞,白洞变为黑洞。在飞行过程中,可以选择在一个虫洞停留1个单位时间,如果当前为白洞,则不消耗燃料,否则消耗s[i]的燃料。现在请你求出从虫洞1到N最少的燃料消耗,保证一定存在1到N的路线。 

输入

第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。 

输出

一个整数,表示最少的燃料消耗。

样例输入 Copy

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

样例输出 Copy

130

提示

样例解释: 
按照1->3->4的路线。  

对于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 
 
解法:dp,分层图。
个人dp较弱,还没调出来,于是强行建图跑dijkspa喽...
dijkspa万岁!
(其实只是spfa)
个人认为分层图的精髓就在于+n建图,有些类似种群并查集吧。考场上因为没有+n,所以崩溃了...
#include<bits/stdc++.h>
using namespace std;
const int maxn=50005;
int h[maxn],w[maxn],o[maxn];
int n,m;
struct edge
{
    int dis,to,next;
}e[maxn<<2];
int head[maxn],cnt;
inline void addedge(int from,int to,int dis)
{
    e[++cnt].next=head[from];
    e[cnt].to=to;
    e[cnt].dis=dis;
    head[from]=cnt;
}
void add()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&h[i]);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&o[i]);
    }
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(h[x]==h[y])//如果颜色一样的两点,不额外耗费燃料,所以在两点间建一个原边,分层,在上下两层的点之间建边
        {
            addedge(x,y+n,z);//坑逼建图
            addedge(x+n,y,z);
        }
        else
        {
            int delta=abs(w[x]-w[y]);
            addedge(x+n,y+n,z+delta);//不一样,黑白之间建边
            addedge(x,y,max(z-delta,0));
        }
    }
    for(int i=1;i<=n;i++)
    {
        addedge(i,i+n,0);//在黑白点之间建立转化点
        addedge(i+n,i,o[i]);
    }
}
int dis[maxn],vis[maxn];
struct cmp
{
    bool operator() (int a,int b)
    {
        return dis[a]>dis[b];
    }
};
priority_queue< int , vector < int > , cmp > q;
void dijkspa()
{
    int s=1;//这里,起点也许就要等待,所以需要特判一发,被坑了2个点...
    if(h[1]==1)
    s=n+1;
    for(int i=1;i<=2*n;i++)
    {
        dis[i]=0x7fffffff;
        vis[i]=0;
    }
    q.push(s);
    vis[s]=1;
    dis[s]=0;
    while(!q.empty())
    {
        int u=q.top();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(dis[v]>dis[u]+e[i].dis)
            {
                dis[v]=dis[u]+e[i].dis;
                if(vis[v]==0)
                {
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
}
int main()
{
    add();
    dijkspa();
    printf("%d",min(dis[n],dis[n+n]));//终点是黑是白不确定,所以也要比较。
    return 0;
}

(完)

posted @ 2019-05-08 00:50  阿基米德的澡盆  阅读(129)  评论(0编辑  收藏  举报