洛谷-P3406 海底高铁:差分和前缀和

题目描述

        该铁路经过N个城市,每个城市都有一个站。不过,由于各个城市之间不能协调好,于是乘车每经过两个相邻的城市之间(方向不限),必须单独购买这一小段的车票。第i段铁路连接了城市i和城市i+11i<N。如果搭乘的比较远,需要购买多张车票。第i段铁路购买纸质单程票需要Ai博艾元。

        虽然一些事情没有协调好,各段铁路公司也为了方便乘客,推出了IC卡。对于第i段铁路,需要花Ci博艾元的工本费购买一张IC卡,然后乘坐这段铁路一次就只要扣Bi(Bi<Ai)元。IC卡可以提前购买,有钱就可以从网上买得到,而不需要亲自去对应的城市购买。工本费不能退,也不能购买车票。每张卡都可以充值任意数额。对于第i段铁路的IC卡,无法乘坐别的铁路的车。

        某员工现在需要出差,要去M个城市,从城市P1出发分别按照P1,P2,P3PM的顺序访问各个城市,可能会多次访问一个城市,且相邻访问的城市位置不一定相邻,而且不会是同一个城市。现在他希望知道,出差结束后,至少会花掉多少的钱,包括购买纸质车票、买卡和充值的总费用。

题目分析

        我们可以这样分析:我们想知道最少花费,我们就需要比较买卡再买票的钱与直接买纸质票的钱,谁大谁小。于是我们可以用贪心思想取每段路程的花费最小,将局部最优解之和当成全局最优解,而通过对此题分析可以得出结论,局部最优解之和就是全局最优解。

        在此基础上,我们继续进行反推。如何求局部解的和,此时我们自然想到了这种方法:每出差一趟,我们就把途径站的花费都加一遍,例如:某员工先从3出发去1,再去4,再去1,则我们需要将第一躺1-2段、2-3段的花费求和,第二趟的1-2段、2-3段、3-4段求和、第三趟3-4段、2-3段、1-2段求和,最后把所有的和加起来。

        那么很显然,我们做了很多的重复计算。所以在此基础上,我们继续进行改进,我们可以通过减少运算的角度来想,即求出某员工出差过程中经过各个城市的次数,再将经过的次数与单次最少花费相乘得出总路程的最小花费。

        在此基础上,我们再继续进行反推。如何求经过各个城市的次数?我们可以这样想:某员工从3到1再到4,这过程可以看成一种前缀和,即在31的过程包含1-2段和2-3段,经过1-22-3的次数加1,在14的过程中经过1-2段、2-3段、3-4段,经过1-2段、2-3段、3-4段的次数加1。所以经过各个城市的次数可以通过一组差分数组来求,而差分数组的和就是经过每个城市的次数。

        所以,此题的思路就清晰了:输入数据进行存储、初始化相关的变量、通过先差分再求前缀和的方法求经过每个城市的次数、次数乘以最小花费得出全局最优解。

代码展示

#include<iostream>
#include <time.h>
using namespace std;

int n, m;  //n个城市,去m个地方
typedef long long ll;
const int maxn = 1e5 + 10;
ll p[maxn] ,a[maxn],b[maxn],c[maxn],sum[maxn],d[maxn], money;
//p去的城市,
//a是纸票价,b是买卡后的价,c是买卡钱
//sum是前缀和
//d是计数数组

void counts(int a, int b)//求经过次数
{
	d[a] += 1;
	d[b] -= 1;
}
//求前缀和
void get_sum()
{
	for (int i = 1; i <= n - 1; i++)
	{
		sum[i] = sum[i-1] + d[i];
	}
}

int main()
{
	clock_t start, finish;
	//clock_t为CPU时钟计时单元数
	//start = clock();//计时
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		cin >> p[i];//输入站台顺序
	}
	for (int i=2; i <= m; i++)
	{
		ll a = p[i - 1], b = p[i];
		if (a > b)
		{
			swap(a, b);
		}
		//差分思想
		counts(a, b);
	}
	get_sum();
	for (int i = 1; i <= n - 1; i++)
	{
		cin >> a[i] >> b[i] >> c[i];
		//求单次最小花费
		money += min(a[i] * sum[i], b[i] * sum[i] + c[i]);
	}
	cout << money;
	 //finish = clock(); 
	//clock()函数返回此时CPU时钟计时单元数
	//cout << endl << "the time cost is:" << double(finish - start) / CLOCKS_PER_SEC << endl;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

littleFive.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值