ccf 2017-12-4行车路线

dijkstra变形,注意点:
1. 虽然题目说最终答案不会超过 10^6,但是中间过程可能超int啊…超了以后溢出变小,可能会影响到最后的解….所以还是乖乖用long long 存储吧…
2. 图的话…一定要考虑重边和反向边!!!

还是只有70分….看到网上直接用sum[i]记录以当前小路为结尾的前面的连续小路之和,发现自己用了两个数组记录…又做繁了….懒得改了…

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<vector>
#include<string>
#include<limits.h>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<functional>
#define iter(i,start,end) for(int (i)=(start),(i)<(end);(i)++)
#define ll long long 
#define pii pair<ll ,int>

using namespace std;
struct cmp         
{
    bool operator()(pii a,pii b)
    {
        return a.first<b.first;
    }
};
struct edge
{
    int v,u,type,wei;       //type为1就是小道
    edge(int a,int b,int c,int d):v(a),u(b),type(c),wei(d){};
};
const int nodemax=500;
const int edgemax=pow(10,5);
const ll inf=1e18;
int n,m; 
vector<edge>edges;
vector<int>g[nodemax];
ll d[nodemax][2];
int p[nodemax][2];      //前继节点   
bool vis[nodemax];

int before[nodemax];        //这个点的d[i][0]之前有几个小路

void init();
void addedge(int a,int b,int c,ll d );
void solve();

int main()
{
    //freopen("2017-12-4.txt","r",stdin);
    while(scanf("%d%d",&n,&m)==2 )
    {
        init();
        solve();
    }
    return 0;
}

void solve()
{
    memset(before, 0, sizeof(before));
    for (int i = 1; i < n; i++) { d[i][0] = d[i][1] = inf;  before[i] = 1; }
    d[0][0]=d[0][1]=0;
    p[0][0]=p[0][1]=-1;         //第一个节点

    memset(vis,0,sizeof(vis));
    priority_queue<pii,vector<pii >,greater<pii > > q;
    q.push(pii(0,0));

    while(q.size())
    {
        pii temp=q.top();
        q.pop();
        int curnode=temp.second;
        if(vis[curnode])    continue;
        vis[curnode]=true;

        //check
            //cout<<"当前处理节点:"<<curnode<<": "<<d[curnode][0]<<"\t"<<d[curnode][1]<<endl;

        for(int i=0;i<g[curnode].size();i++)
        {
            edge cure=edges[g[curnode][i]];
            int u=cure.u, type=cure.type;
            ll wei=cure.wei;
            if(vis[u])
                continue;
            if(type)        //小路
            {
                //check
                    //cout<<"\t处理小路 到"<<u<<endl;
                //第一种情况,之前是大路到达父节点
                if(d[u][type]>d[curnode][0]+pow(wei,2))
                {
                    d[u][type] = d[curnode][0] + pow(wei, 2);
                    p[u][type]=curnode;
                    q.push(pii(d[u][type],u));

                    //check
                        //cout<<"\t\t压入"<<d[u][type]<<endl;
                }

                //第二种情况:之前也是小路
                ll xiaolu=0;
                int par=curnode; //如果无法往前追溯的话 父节点就是当前的节点
                for(int j=0;j<before[curnode];j++)
                {
                    int now=par;
                    par=p[par][type];
                    for(int k=0;k<g[par].size();k++)
                        if(edges[g[par][k]].u==now && edges[g[par][k]].type)    //找到和父节点相邻的那条边  确定是小路的话
                            {xiaolu+=edges[g[par][k]].wei;    break;}

                }
                xiaolu+=wei;
                ll temp=d[par][0]+pow(xiaolu,2);

                if(d[u][type]>temp)             //相等的情况下默认是走大路划算!
                {
                    d[u][type]=temp;
                    before[u]=before[curnode]+1;        //否则就是默认为0
                    p[u][type]=curnode;
                    q.push(pii(temp,u));

                    //check
                        //cout<<"\t\t压入"<<d[u][type]<<endl;
                }
            }
            else
            {
                ll temp=min(d[curnode][0],d[curnode][1])+wei;
                if(d[u][type]>temp)
                {
                    d[u][type]=temp;        //刷表更新
                    p[u][type]=curnode;     //更新父亲节点方便回溯
                    if(temp < d[u][1-type]){ //能少压就少压...
                        q.push(pii(temp,u));

                        //check
                            //cout<<"\t\t压入"<<d[u][type]<<endl;
                    }
                }
            }
        }
    }

    cout<<min(d[n-1][0],d[n-1][1])<<endl;
}


void init()
{
    edges.clear();
    memset(g,0,sizeof(g));

    for(int i=0;i<m;i++)
    {
        int u,v,type;   ll wei;
        cin>>type>>u>>v>>wei;
        addedge(u-1,v-1,type,wei);  //序号从0开始
    }
}

void addedge(int from ,int to ,int type,ll wei)
{
    int i;
    for(i=0;i<g[from].size();i++)
    {
        if(edges[g[from][i]].u==to && edges[g[from][i]].type==type)
        {
            if(edges[g[from][i]].wei > wei)
                edges[g[from][i]].wei=wei;
            break;
        }
    }
    if(i==g[from].size())   //不是重复边
    {
        edges.push_back(edge(from,to,type,wei));        //可不可以考虑不存u
        g[from].push_back(edges.size()-1);
    } 

    for(i=0;i<g[to].size();i++)
    {
        if(edges[g[to][i]].u==from &&edges[g[to][i]].type==type)
        {
            if(edges[g[to][i]].wei > wei)
                edges[g[to][i]].wei=wei;
            break;
        }
    }
    if(i==g[to].size())   
    {
        edges.push_back(edge(to,from,type,wei));        //可不可以考虑不存u
        g[to].push_back(edges.size()-1); 
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值