洛谷 P1220 关路灯
题目链接
题目描述
某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。
为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电。他每天都是在天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边的路灯,而事实并非如此,因为在关的过程中适当地调头有可能会更省一些。
现在已知老张走的速度为1m/s,每个路灯的位置(是一个整数,即距路线起点的距离,单位:m)、功率(W),老张关灯所用的时间很短而可以忽略不计。
请你为老张编一程序来安排关灯的顺序,使从老张开始关灯时刻算起所有灯消耗电最少(灯关掉后便不再消耗电了)。
题目分析
老张从一盏灯到另一盏的途中的所有灯都一定要顺手关上——使用贪心策略,关灯不耗费时间,关上一定会更优。因此,所有关上的灯将形成一个区间,而老张一定会站在区间的两端,而他要去关的下一盏灯一定是区间左边那一盏灯或者右边那一盏灯——根据以上的贪心策略,他不能越过某一盏灯。
那么,我们把当前已关的灯的左端点和右端点作为两个参数,把老张站在左端点还是右端点作为第三个参数,便可以进行记忆化搜索。而每一步的耗电量即为老张该步移动的时间与当前还亮着的灯的单位时间耗电量的乘积。对于当前还亮着的灯的单位时间耗电量,由于关着的灯的是一个区间,我们只需要用所有灯的耗电量减去该区间的耗电量即可。该区间的耗电量只需要使用前缀和计算就行了。
代码
1 #include<algorithm> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 long long n,c,p[51],v[51],sum[51],f[51][51][2]; 6 void dfs(long long x,long long y,bool option) 7 { 8 if(x>1&&option&&f[x-1][y][0]>f[x][y][1]+(p[y]-p[x-1])*(sum[n]-sum[y]+sum[x-1])) 9 { 10 f[x-1][y][0]=f[x][y][1]+(p[y]-p[x-1])*(sum[n]-sum[y]+sum[x-1]); 11 dfs(x-1,y,false); 12 } 13 if(y<n&&option&&f[x][y+1][1]>f[x][y][1]+(p[y+1]-p[y])*(sum[n]-sum[y]+sum[x-1])) 14 { 15 f[x][y+1][1]=f[x][y][1]+(p[y+1]-p[y])*(sum[n]-sum[y]+sum[x-1]); 16 dfs(x,y+1,true); 17 } 18 if(x>1&&!option&&f[x-1][y][0]>f[x][y][0]+(p[x]-p[x-1])*(sum[n]-sum[y]+sum[x-1])) 19 { 20 f[x-1][y][0]=f[x][y][0]+(p[x]-p[x-1])*(sum[n]-sum[y]+sum[x-1]); 21 dfs(x-1,y,false); 22 } 23 if(y<n&&!option&&f[x][y+1][1]>f[x][y][0]+(p[y+1]-p[x])*(sum[n]-sum[y]+sum[x-1])) 24 { 25 f[x][y+1][1]=f[x][y][0]+(p[y+1]-p[x])*(sum[n]-sum[y]+sum[x-1]); 26 dfs(x,y+1,true); 27 } 28 return; 29 } 30 int main() 31 { 32 memset(f,0x7f/3,sizeof(f)); 33 scanf("%lld%lld",&n,&c); 34 for(long long i=1;i<=n;++i) 35 { 36 scanf("%lld%lld",&p[i],&v[i]); 37 sum[i]=sum[i-1]+v[i]; 38 } 39 f[c][c][0]=f[c][c][1]=0; 40 dfs(c,c,0); 41 printf("%lld",min(f[1][n][0],f[1][n][1])); 42 return 0; 43 }