关路灯题解

Description

某一村庄在一条路线上安装了n盏路灯,每盏灯的功率(单位时间的耗电量)有大有小。老张就住在这条路中间某 一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。为了给村里节省电费,老张记录下了每盏 路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电。他每天都是在 天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。开始他以为,先算一下左边路灯的总功率 ,再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边的路灯,这样可以最省电。 而事实并非如此,因为在关的过程中适当地调头有可能会更省一些。现在已知老张走的速度为1米/秒;每个路灯的 位置(是一个整数,即距路线起点的距离,单位:米);以及功率(W),老张关灯所用的时间很短而可以忽略不 计。请你为老张编一程序来安排关灯的顺序,使从老张开始关灯时刻算起所有灯消耗电最少(灯关掉后便不再消耗 电了)。

Format

Input

第1行是两个数字n和c,分别表示路灯数和老张所处位置的路灯号;

第2行至第n+1行,每行有两个整数。

其中第k+1行的第一个整数表示第k盏灯离路线起点的距离,第二个整数表示第k盏灯的功率。

以上n+1行中,每行的两个整数之间都有一个空格分隔。

1≤n≤1000,求得的最小耗电量不大于1×10^8

Output

只有一行,该行只有一个整数,表示求得的最少耗电量。(单位:J,1J=1W·秒)。

Samples

输入数据 1

5 3
2 10
3 20
5 20
6 30
8 10

Copy

输出数据 1

270

Copy

Hint 此时关灯顺序为34215,不必输出这个关灯顺序 有5盏灯,老张从第3盏灯开始关灯,最小耗电量 =1*30+4*20+5*10+11*10=270

思路

 首先,我们发现,这道题像P1063 [NOIP2006 提高组] 能量项链【Zju1602 】乘法游戏一样,都可以用合并Dp。

然后,再想怎么做,不难发现,我们可以用一个数组:

f[左边界][右边界][位置]

来储存时间。

再用前缀和:

	for(int i=1;i<=n;i++){
    	cin>>z[i]>>x[i];
    	ld[i]=ld[i-1]+x[i];
    }

储存没用数据。

然后,不难分析出关键代码:

	f[m][m][1]=0;
	f[m][m][0]=0;
	for(int i=2;i<=n;i++){
		for(int j=1;j<=n;j++){
			long long zs=i+j-1;
   			f[j][zs][0]=min(f[j+1][zs][0]+(z[j+1]-z[j])*(ld[j]+ld[n]-ld[zs]),f[j+1][zs][1]+(z[zs]-z[j])*(ld[j]+ld[n]-ld[zs]));
			f[j][zs][1]=min(f[j][zs-1][0]+(z[zs]-z[j])*(ld[j-1]+ld[n]-ld[zs-1]),f[j][zs-1][1]+(z[zs]-z[zs-1])*(ld[j-1]+ld[n]-ld[zs-1]));	   
		}
	}

而其中最关键的,只有三行:

			long long zs=i+j-1;
   			f[j][zs][0]=min(f[j+1][zs][0]+(z[j+1]-z[j])*(ld[j]+ld[n]-ld[zs]),f[j+1][zs][1]+(z[zs]-z[j])*(ld[j]+ld[n]-ld[zs]));
			f[j][zs][1]=min(f[j][zs-1][0]+(z[zs]-z[j])*(ld[j-1]+ld[n]-ld[zs-1]),f[j][zs-1][1]+(z[zs]-z[zs-1])*(ld[j-1]+ld[n]-ld[zs-1]));	   

或者两行:


   			f[j][i+j-1][0]=min(f[j+1][i+j-1][0]+(z[j+1]-z[j])*(ld[j]+ld[n]-ld[i+j-1]),f[j+1][i+j-1][1]+(z[i+j-1]-z[j])*(ld[j]+ld[n]-ld[i+j-1]));
			f[j][i+j-1][1]=min(f[j][i+j-1-1][0]+(z[i+j-1]-z[j])*(ld[j-1]+ld[n]-ld[i+j-1-1]),f[j][i+j-1-1][1]+(z[i+j-1]-z[i+j-1-1])*(ld[j-1]+ld[n]-ld[i+j-1-1]));	   

完整代码见下:

#include<bits/stdc++.h>
using namespace std;
struct ll{
	long long a,b,c,d,e,k,x,y,z;
}v[102345];
bool qp(ll a,ll b){
	return a.a<b.a;
}
long long mo=1e9+7,f[1005][1050][5],ld[1000405],lk=0,n,m,z[14045],x[20122];
int main(){
	cin>>n>>m;
	memset(f,10,sizeof(f));
	for(int i=1;i<=n;i++){
    	cin>>z[i]>>x[i];
    	ld[i]=ld[i-1]+x[i];
    }
	f[m][m][1]=0;
	f[m][m][0]=0;
	for(int i=2;i<=n;i++){
		for(int j=1;j<=n;j++){
			long long zs=i+j-1;
   			f[j][zs][0]=min(f[j+1][zs][0]+(z[j+1]-z[j])*(ld[j]+ld[n]-ld[zs]),f[j+1][zs][1]+(z[zs]-z[j])*(ld[j]+ld[n]-ld[zs]));
			f[j][zs][1]=min(f[j][zs-1][0]+(z[zs]-z[j])*(ld[j-1]+ld[n]-ld[zs-1]),f[j][zs-1][1]+(z[zs]-z[zs-1])*(ld[j-1]+ld[n]-ld[zs-1]));	   
		}
	}
	cout<<min(f[1][n][1],f[1][n][0]);
	return 0;
}

 

 

 

 

 

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
06-01
这道题是一道典型的费用限制最短路题目,可以使用 Dijkstra 算法或者 SPFA 算法来解决。 具体思路如下: 1. 首先,我们需要读入输入数据。输入数据中包含了道路的数量、起点和终点,以及每条道路的起点、终点、长度和限制费用。 2. 接着,我们需要使用邻接表或邻接矩阵来存储图的信息。对于每条道路,我们可以将其起点和终点作为一个有向边的起点和终点,长度作为边权,限制费用作为边权的上界。 3. 然后,我们可以使用 Dijkstra 算法或 SPFA 算法求解从起点到终点的最短路径。在这个过程中,我们需要记录到每个点的最小费用和最小长度,以及更新每条边的最小费用和最小长度。 4. 最后,我们输出从起点到终点的最短路径长度即可。 需要注意的是,在使用 Dijkstra 算法或 SPFA 算法时,需要对每个点的最小费用和最小长度进行松弛操作。具体来说,当我们从一个点 u 经过一条边 (u,v) 到达另一个点 v 时,如果新的费用和长度比原来的小,则需要更新到达 v 的最小费用和最小长度,并将 v 加入到优先队列(Dijkstra 算法)或队列(SPFA 算法)中。 此外,还需要注意处理边权为 0 或负数的情况,以及处理无法到达终点的情况。 代码实现可以参考以下样例代码: ```c++ #include <cstdio> #include <cstring> #include <queue> #include <vector> using namespace std; const int MAXN = 1005, MAXM = 20005, INF = 0x3f3f3f3f; int n, m, s, t, cnt; int head[MAXN], dis[MAXN], vis[MAXN]; struct Edge { int v, w, c, nxt; } e[MAXM]; void addEdge(int u, int v, int w, int c) { e[++cnt].v = v, e[cnt].w = w, e[cnt].c = c, e[cnt].nxt = head[u], head[u] = cnt; } void dijkstra() { priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); dis[s] = 0; q.push(make_pair(0, s)); while (!q.empty()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; i != -1; i = e[i].nxt) { int v = e[i].v, w = e[i].w, c = e[i].c; if (dis[u] + w < dis[v] && c >= dis[u] + w) { dis[v] = dis[u] + w; q.push(make_pair(dis[v], v)); } } } } int main() { memset(head, -1, sizeof(head)); scanf("%d %d %d %d", &n, &m, &s, &t); for (int i = 1; i <= m; i++) { int u, v, w, c; scanf("%d %d %d %d", &u, &v, &w, &c); addEdge(u, v, w, c); addEdge(v, u, w, c); } dijkstra(); if (dis[t] == INF) printf("-1\n"); else printf("%d\n", dis[t]); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值