zoukankan      html  css  js  c++  java
  • P1220 关路灯

    题目描述

    某一村庄在一条路线上安装了 (n) 盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。

    为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电。他每天都是在天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边的路灯,而事实并非如此,因为在关的过程中适当地调头有可能会更省一些。

    现在已知老张走的速度为 (1m/s),每个路灯的位置(是一个整数,即距路线起点的距离,单位:(m))、功率((W)),老张关灯所用的时间很短而可以忽略不计。

    请你为老张编一程序来安排关灯的顺序,使从老张开始关灯时刻算起所有灯消耗电最少(灯关掉后便不再消耗电了)。

    输入格式

    第一行是两个数字 (n)(表示路灯的总数)和 (c)(老张所处位置的路灯号);

    接下来 (n) 行,每行两个数据,表示第 (1) 盏到第 (n) 盏路灯的位置和功率。数据保证路灯位置单调递增。

    输出格式

    一个数据,即最少的功耗(单位:(J)(1J=1W imes s))。

    输入输出样例

    输入
    5 3
    2 10
    3 20
    5 20
    6 30
    8 10
    
    输出
    270
    

    说明/提示

    样例解释

    此时关灯顺序为 (3) (4) (2) (1) (5)

    数据范围

    (1le nle50,1le cle n)

    Solution

    一道需要分析题目性质的区间 (dp) 的题目,思维考验的很到位。
    首先分析一下为什么本题的做法是区间 (dp:)
    注意到老张是从一个点开始往左边或右边走,且经过的点的灯一定都是关的,所以说,[ (a_i<a_j<a_k),且 (a_i)(a_k) 的位置的灯已经关了,而 (a_j) 这个位置的灯没关 ] 这种情况这是不可能的。因为无论老张是从 (a_i) 走到 (a_k) 还是从 (a_k) 走到 (a_i),他都一定会经过 (a_j),那么他选择关上 (a_j) 位置的灯一定是最优的。
    这么说来,老张所关的灯一定是在一个连续的区间,这就启发我们利用区间 (dp) 来解决这道题。
    还有一个性质:
    老张每关一盏灯,他一定处于这个区间的端点。
    这一点很好理解,因为老张想要关灯,他就要让这个区间不断地向左向右扩展,那么他一定是要走出原来的区间的,这样他所在的位置就一定是区间端点。
    假设老张已经扩展的区间是 ([l,r]),且老张在区间的左端点 (l) 处,那么他下一步有两种选择:(①)关掉第 (l-1) 盏灯;(②)关掉第 (r+1) 盏灯。
    如果他要去关第 (l-1) 盏灯,他需要花 (a_{l}-a_{l-1}) 的时间,在此期间,(1)$l-1,r+1$(n) 的灯一直亮着,那么代价就是 ((a_l-a_{l-1})*(S[1][l-1]+S[r+1][n]))
    如果他要去关第 (r+1) 盏灯,他需要花 (a_{r+1}-a_l) 的时间,在此期间,(1)$l-1,r+1$(n) 的灯一直亮着,那么代价就是 ((a_{r+1}-a_{l})*(S[1][l-1]+S[r+1][n]))
    ((S[i][j]) 表示 (i)~(j) 的路灯 (1s) 所消耗的电能和())
    由此可见,老张在区间左右端点的位置不同,所影响的花费时间不同,进而影响不同的代价。
    所以我们要再开一维来记录老张此时是在区间的左端点还是右端点。
    那么状态也就随之出来了。

    状态设置

    (dp[l][r][0/1]) 表示老张已经将 ([l,r]) 内的灯关了,且老张此时是在左端点(()(0)表示())还是在右端点(()(1)表示())

    状态转移

    我是用填表法来做的。
    考虑 (dp[l][r][0]) 由什么转移到:
    既然老张是在区间的左端点,即 (a_l) 处,那么也就说明第 (l) 盏灯是老张刚关上的,那么上一步的区间应该是 ([l+1,r])
    如果老张原来是在 (a_{l+1}) 处,要走 (a_{l+1}-a_l) (s),代价为 ((a_{l+1}-a_l)*(S[1][l]+S[r+1][n]))
    如果老张原来是在 (a_r) 处,要走 (a_r-a_l) (s),代价为 ((a_r-a_l)*(S[1][l]+S[r+1][n]))
    两者取 (min) 即可:
    (dp[l][r][0]=min(dp[l+1][r][0]+(a_{l+1}-a_l)*(S[1][l]+S[r+1][r]),dp[l+1][r][1]+(a_r-a_l)*(S[1][l]+S[r+1][n])))

    同理得:

    (dp[l][r][1]=min(dp[l][r-1][0]+(a_r-a_l)*(S[1][l-1]+S[r][n]),dp[l][r-1][1]+(a_r-a_{r-1})*(S[1][l-1]+S[r][n])))

    那么这个题就做完了。

    Code

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #define db double
    #define ll long long
    using namespace std;
    inline int read()
    {
    	char ch=getchar();
    	int a=0,x=1;
    	while(ch<'0'||ch>'9')
        {
        	if(ch=='-') x=-x;
        	ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		a=(a<<1)+(a<<3)+(ch^48);
    		ch=getchar();
    	}
    	return a*x;
    }
    const int N=200;
    int n,m;
    int a[N],b[N],S[N][N],dp[N][N][2];
    int dis(int x,int y)     //求第x盏灯和第y盏灯之间的距离 
    {
    	return abs(a[x]-a[y]);
    }
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;i++) 
    	{
    		a[i]=read();b[i]=read();
    	}
    	for(int i=1;i<=n;i++)
    	    for(int j=i;j<=n;j++)
    	        for(int k=i;k<=j;k++)
    	            S[i][j]+=b[k];      //预处理第i~j盏灯1s所消耗的电能 
    	memset(dp,0x3f,sizeof(dp));
    	dp[m][m][0]=dp[m][m][1]=0;      //边界条件:老张一开始在第m盏灯,所以代价为0 
    	for(int len=2;len<=n;len++)
    	{
    		for(int l=1;l+len-1<=n;l++)
    		{
    			int r=l+len-1;
    			dp[l][r][0]=min(dp[l+1][r][0]+dis(l+1,l)*(S[1][l]+S[r+1][n]),dp[l+1][r][1]+dis(r,l)*(S[1][l]+S[r+1][n]));
    			dp[l][r][1]=min(dp[l][r-1][0]+dis(l,r)*(S[1][l-1]+S[r][n]),dp[l][r-1][1]+dis(r-1,r)*(S[1][l-1]+S[r][n]));
    		}
    	}
    	printf("%d
    ",min(dp[1][n][0],dp[1][n][1]));  //最后老张在左端右端都行,取min 
    	return 0;
    }
    
  • 相关阅读:
    Atitit.播放系统规划新版本 v4 q18 and 最近版本回顾
    Atitit.播放系统规划新版本 v4 q18 and 最近版本回顾
    atitit.极光消息推送服务器端开发实现推送  jpush v3. 总结o7p
    atitit.极光消息推送服务器端开发实现推送  jpush v3. 总结o7p
    Atitit.文件搜索工具 attilax 总结
    Atitit.文件搜索工具 attilax 总结
    Atitit.软件命名空间  包的命名统计 及命名表(2000个名称) 方案java package
    Atitit.软件命名空间  包的命名统计 及命名表(2000个名称) 方案java package
    Atitit..状态机与词法分析  通用分词器 分词引擎的设计与实现 attilax总结
    Atitit..状态机与词法分析  通用分词器 分词引擎的设计与实现 attilax总结
  • 原文地址:https://www.cnblogs.com/xcg123/p/14050500.html
Copyright © 2011-2022 走看看