P1220 关路灯
导入
1.假如你是老头,你每次关灯最多有两个选择: 一.关最左边的灯 二.关最右边的灯
而你的目的是:使总耗电量最小
Q:那我能不能每次选去关功率大的那个灯呢?
A:不行,因为耗电量还与时间有关
Q:那我能不能每次选去关 路程(时间)\(*\)功率 较大的灯(即贪心)呢?
A:不行,假设这样一个场景:假设初始在第三个位置(总共8个位置),你的左边有一个功率非常小的灯A=1,左边的左边有一个功率非常大的灯B=1000000,而右边功率都大于A(两位数),如果贪心的话,就会一直选右边,导致B的耗电量巨大。这时候只要不贪心,先把B关掉,这样总耗电量就小了
深入
克服贪心,把当前看起来不划算的选项也算上,这就叫dp(也可以记忆化搜索穷举)。
该如何设计呢?
对于任意一段给定的序列,最后关掉的灯不是左边就是右边。
所以设\(dp[i][j][op](op\in\{0,1\})\)为关掉 i~j 内所有灯时的当前的所有灯的总耗电量,而非i~j的总耗电量,其中op=0时代表最后关掉的灯在最左端,1代表最右端。对于任一状态而言都是由前两种状态变化而来,而最终答案是两种状态中较小的那个。
又因为必须要从特定点出发,因此某些状态是无法求得的,设无法求得的状态为无穷大,这样如果当前状态的两个前置状态都是无穷大,代表这个状态无法到达,也是无穷大,即只要有能到达的状态就不是无穷大。
代码
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f
struct
{
int pos;
int power;
}d[55];
int cons[55][55][2]={0};
int sum[55]={0};
int main()
{
int n,c;
scanf("%d%d",&n,&c);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&d[i].pos,&d[i].power);
sum[i]=sum[i-1]+d[i].power;
}
memset(cons,inf,sizeof cons);
cons[c][c][0]=cons[c][c][1]=0;
for(int k=2;k<=n;k++)
{
for(int i=1;i+k-1<=n;i++)
{
int l=i,r=i+k-1;
cons[l][r][0]=min(cons[l+1][r][0]+(sum[n]-(sum[r]-sum[l]))*(d[l+1].pos-d[l].pos),cons[l+1][r][1]+(sum[n]-(sum[r]-sum[l]))*(d[r].pos-d[l].pos));
cons[l][r][1]=min(cons[l][r-1][0]+(sum[n]-(sum[r-1]-sum[l-1]))*(d[r].pos-d[l].pos),cons[l][r-1][1]+(sum[n]-(sum[r-1]-sum[l-1]))*(d[r].pos-d[r-1].pos));
}
}
printf("%d\n",min(cons[1][n][0],cons[1][n][1]));
return 0;
}

浙公网安备 33010602011771号