洛谷 P1220 关路灯 区间dp
题面
https://www.luogu.com.cn/problem/P1220
分析
考虑区间\(dp\)
令\(dp[l][r][0]\)为已经关了\([l,r]\)的灯,当前位置在\(l\)的最少功耗;
\(dp[l][r][1]\)为已经关了\([l,r]\)的灯,当前位置在\(r\)的最少功耗。
可以发现,因为状态是区间的扩大,故只有如下情况:
若当前为\(dp[l][r][0]\),上一个状态要么是从\(l+1\)点向左走了\(1\)格,即\(dp[l+1][r][0]+cost(l,l+1)\);或者是从\(r\)点向左走了\(r-l+1\)格,即\(dp[l+1][r][1]+cost(l,r)\)
若当前为\(dp[l][r][1]\),上一个状态要么是从\(r-1\)点向右走了\(1\)格,即\(dp[l][r-1][1]+cost(r-1,r)\);或者是从\(l\)点向右走了\(r-l+1\)格,即\(dp[l][r-1][0]+cost(l,r)\)
考虑\(cost\)的计算:
可以发现,cost的计算与跑动的时间、目前仍在运行的灯数均有关。当从\(x\)跑到\(y\)时,且当前\([l,r]\)的灯已被关闭时:
目前仍在运行的灯数(功率)为\(sum[l-1]+(sum[n]-sum[r])\),跑动时间为\((y-x)\)。
故cost函数可以写为:
int count(int x, int y, int l, int r) {
return (pos[y] - pos[x]) * (sum[l - 1] + (sum[n] - sum[r]));
}
Code
#include<iostream>
#include<cstdio>
#include<cstring>
const int maxn = 50 + 10;
const int inf = 0x3f3f3f3f;
int n, c;
int pos[maxn], p[maxn], sum[maxn];
int dp[maxn][maxn][2];
int count(int x, int y, int l, int r) {
return (pos[y] - pos[x]) * (sum[l - 1] + (sum[n] - sum[r]));
}
int main() {
memset(dp, inf, sizeof(dp));
scanf("%d%d", &n, &c);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &pos[i], &p[i]);
sum[i] = sum[i - 1] + p[i];
//dp[i][i][0]=dp[i][i][1]=0;
}
dp[c][c][0] = dp[c][c][1] = 0;
for (int len = 2; len <= n; len++)
for (int i = 1; i + len - 1 <= n; i++) {
int j = i + len - 1;
dp[i][j][0] = std::min(dp[i + 1][j][0] + count(i, i + 1, i + 1, j),
dp[i + 1][j][1] + count(i, j, i + 1, j));
dp[i][j][1] = std::min(dp[i][j - 1][1] + count(j - 1, j, i, j - 1),
dp[i][j - 1][0] + count(i, j, i, j - 1));
}
int ans = std::min(dp[1][n][0], dp[1][n][1]);
printf("%d", ans);
return 0;
}

浙公网安备 33010602011771号