【重庆市NOIP模拟赛】业务

原创 2016年08月28日 21:38:22

题目描述

Mr_H 谋得一份兼职——货车司机,从此以后他将会开着货车穿行在 C 国的各大城市之间。
C 国中有 n 座城市(编号为 1~n),并且有 m 条双向公路,每条公路连接两座不同的城市。货车
从任意一座城市出发都可以抵达任意另一座城市。在每条公路上,都有一个收费站,通过的车辆需要
交纳一定过路费。可能有多条公路连接相同的两座城市。
为了增加财政收入,C 国还在每座城市也设置了收费站。并且规定,车辆从一座城市到另一座城
市的费用是,所经过公路费用和,加上所经过的城市中费用的次大值
...(这里的次大可以和最大相同,
但是城市不同)。
(这里的次大可以和最大相同,
但是城市不同)。
现在 Mr_H 告诉你今年 k 次业务运送货物的起点、终点城市列表,请你帮忙计算,每次业务需要
交纳的最低过路费。

输入

第一行包含三个用一个空格隔开的整数:n,m,k。其意义如题目描述。
第 2 到第 n+1 行:第 i+1 行包含一个单独的整数 c(1<=c<=100000),表示城市 i 的费用。
接下来的 m 行,每行包含三个整数 a,b,w,表示一条公路连接城市 a 和城市 b(1<=a,b<=n),
其过路费为 w(1<=w<=100000)。
最后的 k 行,每行包含两个整数:s,t,表示一次业务的起点和终点(1<=s,t<=n 且 s!=t)。

输出

共 k 行,每行一个整数,表示从城市 s 到 t 的最少过路费。

样例输入

 (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

5 7 3
2
5
3
3
4
1 2 3
1 3 2
2 5 3
5 3 1
5 4 1
2 4 3
3 4 4
1 3
1 4
2 3

样例输出

4
7
8

提示

一个字,这题简直是坑啊。

考试时第一反应就是枚举次小点,枚举起点预处理出每两个点的最短路就行了,时间O(N^3*logM+K)(用堆优化的Dijkstra),骗了30%的分。
后来看了正解,内心简直是崩溃的。
首先假设:
dist[i][0]=从 k 出发到 i 点经过的点费用最大值为 a[k]的情况的最小边费用和;
dist[i][1]=从 k 出发到 i 点经过的点费用次大值为 a[k]的情况的最小边费用和;
一样枚举次小点,但在计算两两最短路时并没有枚举起点,直接用ans[i][j]=min(dist[i][0]+dist[j][1],dist[i][1]+dist[j][0])+Val[k]就行了,时间复杂度为O(N^2*logM+K)

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;

#define MAXN 250
#define MAXM
#define DIST(x)(tmp[x.pos][x.hm])
#define VIS(x)(vis[x.pos][x.hm])
#define INF 0x3f3f3f3f
typedef long long int LL;

int getint()
{
    int rn=0;
    char c=getchar();

    while(c<'0'||'9'<c)c=getchar();
    while('0'<=c&&c<='9')
    {
        rn=rn*10+c-'0';
        c=getchar();
    }

    return rn;
}

int G[MAXN+10][MAXN+10];
int Val[MAXN+10];
int dist[MAXN+10][MAXN+10];
int N,M,K;

int tmp[MAXN+10][2];
bool vis[MAXN+10][2];

struct point
{
    int pos,w;
    bool hm;

    point(){}
    point(int a,bool b,int c){pos=a,hm=b,w=c;}

    bool operator < (const point &a)const
    {
        return w>a.w;
    }
};

priority_queue<point>que;
void insert(point newed)
{
    if(DIST(newed)<newed.w)return;

    DIST(newed)=newed.w;
    que.push(newed);
}

void Dijkstra(int sc)
{
    memset(tmp,0x3f,sizeof(tmp));
    memset(vis,0,sizeof(vis));
    insert(point(sc,0,0));

    point now,newed;
    while(!que.empty())
    {
        now=que.top();que.pop();

        if(VIS(now))continue;
        else VIS(now)=1;

        for(int v=1;v<=N;++v)
            if(G[now.pos][v])
            {
                newed.pos=v;
                newed.hm=now.hm;
                newed.w=DIST(now)+G[now.pos][v];

                if(Val[v]>Val[sc])//v点为最大点
                {
                    if(now.hm)continue;//之前已有最大点,矛盾
                    else
                    {
                        newed.hm=1;    //现在有了最大点
                        insert(newed);
                    }
                }
                else if(Val[v]==Val[sc])//v可能为最大点
                {
                    if(v==sc)insert(newed);//v是次小点
                    else
                    {
                        insert(newed);     //把v当做更小点

                        newed.hm=1;        //把v当做最大点
                        insert(newed);
                    }
                }
                else insert(newed);        //把v当做更小点
            }
    }
}

int main()
{
    //freopen("input25.txt","r",stdin);
    //freopen("business.out","w",stdout);

    scanf("%d%d%d",&N,&M,&K);
    int i,j,k;

    for(i=1;i<=N;++i)
        scanf("%d",&Val[i]);

    int a,b,c;
    for(i=1;i<=M;++i)
    {
        scanf("%d%d%d",&a,&b,&c);
        G[a][b]=(!G[a][b]||G[a][b]>c?c:G[a][b]); //如果有多条路,取较短的一条
        G[b][a]=(!G[b][a]||G[b][a]>c?c:G[b][a]);
    }

    memset(dist,0x3f,sizeof(dist));
    for(int sc=1;sc<=N;++sc)//枚举次小点
    {
        Dijkstra(sc);

        for(i=1;i<=N;++i)
            for(j=1;j<=N;++j)//更新两两距离
                dist[i][j]=min(dist[i][j],min(tmp[i][0]+tmp[j][1],tmp[i][1]+tmp[j][0])+Val[sc]);
    }

    for(i=1;i<=K;++i)
    {
        scanf("%d%d",&a,&b);
        printf("%d\n",dist[a][b]);//直接回复询问
    }

    //fclose(stdin);
    //fclose(stdout);
}

/*
3 2 3
2
3
4
1 2 2
1 3 2
1 2
1 3
2 3
*/


版权声明:本文为博主原创文章,未经博主允许不得转载。

【bzoj 十连测】[noip2016十连测第三场]Problem A: 平均数(二分答案+归并排序求逆序对)

抚琴之人泪满衫,扬花萧萧落

【bzoj 十连测】[noip2016十连测第五场]Problem C: travel(模拟)

墨已入水渡青花,轻揽红霞晚归家

2015重庆市NOIP模拟赛题目+数据

  • 2015年10月04日 16:58
  • 8.2MB
  • 下载

#bzoj2933#【重庆市NOIP模拟赛】数据(DP线段树优化 or DP堆优化 + 证明)

2933: 【重庆市NOIP模拟赛】数据 时间限制: 1 Sec  内存限制: 128 MB 题目描述 Mr_H 出了一道信息学竞赛题,就是给 n 个数排序。输入格式是这样的: 试题有若干组数据。每...

#bzoj2932#【重庆市NOIP模拟赛】旅行(贪心 DP是不可以的!)

2932: 【重庆市NOIP模拟赛】旅行 时间限制: 1 Sec  内存限制: 128 MB 题目描述 Mr_H 旗下的 n 个 OIer 坐船外出旅行! 但是他们只有一艘船,虽然船能装...

【重庆市NOIP模拟赛】数据

数据 时间限制: 1 Sec 内存限制: 128 MB 提交: 58 解决: 31 [提交][状态][我的提交] 题目描述 Mr_H 出了一道信息学竞赛题,就是给 n 个数排序。输入格式...

Noip普及组模拟赛

  • 2017年10月20日 13:42
  • 50KB
  • 下载

noip模拟赛第二试

  • 2011年10月27日 09:30
  • 5.23MB
  • 下载

重庆市noip2016模拟round4 BSOJ4874 排序

想要看随机化的可以看看我的这篇文章 BSOJ4874 -- 【NOIP2016-4】 排序  (noip模拟) Description shell 排序是众多排序算法中的一种。给定 N 个整数,...
  • moep0
  • moep0
  • 2016年10月31日 15:40
  • 258

2011noip模拟赛一试

  • 2011年10月27日 09:26
  • 234KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【重庆市NOIP模拟赛】业务
举报原因:
原因补充:

(最多只允许输入30个字)