街灯を消す
説明
ある村では、あるルートにn個の街灯が設置されており、各街灯の電力は大きいか小さいか(つまり、同じ期間にどれだけの電力が消費されるか)です。ラオ・チャンはこの道の真ん中にある街灯の隣に住んでいて、毎朝夜明けにこれらの街灯を一つずつ消していく仕事です。
村の電気代を節約するために、ラオスチャンは各街灯の位置と電力を記録しました。彼はライトをオフにするたびに、できるだけ早くそれらをオフにしましたが、チャンはオフにする方法を知りませんでした。電力を最も節約するためのライト。彼は毎日夜明けに街灯を消し、それから彼は左または右のどちらかで街灯を消すことができます。最初は、左側の街灯の総電力を計算し、次に右側の街灯の総電力を計算してから、最初に電源側をオフにしてから、元に戻してオフにすることを選択すると考えました。反対側の街灯ですが、オフになっているため、そうではありません。プロセスで適切なUターンを行うと、少し節約できる場合があります。
現在、ラオスチャンの歩行速度は1m / s、各街路灯の位置(整数、つまりルートの始点からの距離、単位:m)、電力(W)、時間であることがわかっています。ライトを消すのにラオスチャンは非常に短く、無視することができます。
Lao Zhangのプログラムをコンパイルして、Lao Zhangがライトをオフにし始めた瞬間からすべてのライトが最小の電力を消費するように、ライトをオフにする順序を調整してください(ライトがオフになった後は電力が消費されなくなります)。
フォーマット
入力フォーマット
最初の行は2つの数字n(0 <n <50、つまり街路灯の総数を意味します)とc(1 <= c <= n Lao Zhangが配置されている街灯番号)です。
次のn行には2つの数字があります。各行のデータは、1番目からn番目の街路灯の位置と電力を表します。
出力フォーマット
1つのデータ、つまり最小消費電力(単位:J、1J = 1W・s)。
例1
サンプル入力1
5 3
2 10
3 20
5 20
6 30
8 10
サンプル出力1
270
制限
テストポイントごとに1秒
促す
このときの消灯の順番は34 2 15です。
問題解決
/*
经典好题~一个dp必学题叭~~
首先我们要想怎么样才能完整表示一个状态~
我们可以看到的
如果去关灯关掉了区间[l,r]的灯
那么最后关的一盏不是l就是r
所以我们可以想到用这样一个状态表示
f[l][r][k]表示关掉区间[l,r]的所有灯所需要的最小代价
其中k=0表示现在人在l即最后关的灯是l
k=1表示现在人在r即最后关的是r这盏灯
这样就设计出了一个正确无误的状态
先考虑初值
f[i][i]就是从起始位置直接到i开i灯的代价
那么我们来考虑状态转移
我们知道f[l][r][0]表示的是最后关掉了l这盏灯使得[l,r]全部都关了
那么对应的上一个状态必然是已经关掉了[l+1,r]区间所有的灯
那么我们就要考虑到到底是f[l+1][r][0]转移过来更优还是f[l+1][r][1]更优
即考虑到了左右两种完全的情况
那么很容易有状态转移
f[i][j][0]=min(f[i+1][j][0]+(sump-(s[i+1][j]))*getd(i,i+1),
f[i+1][j][1]+(sump-(s[i+1][j]))*getd(i,j));
其中我们已经预处理出sump表示所有灯总功率
s[i]表示1...i的所有灯的功率然后用前缀和可以算出s[i+1][j]
这个转移仔细理解一下就好了
同理就可以得出f[i][j][1]的转移
递推的时候外层循环枚举长度内层循环枚举起点
最后取f[1][n][0]和f[1][n][1]的更优值就好了~
时间复杂度O(n^2)
可以直接秒杀~
*/
ACコード
#include <algorithm> //关路灯
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 52;
int n, c;
int x[maxn], w[maxn], sump;
int dp[maxn][maxn][2], s[maxn];
// dp[i][j] 选择区间[i, j]时, 最小消耗
// time[i][j] 表示从i到j的时间
// s[i]功率前缀和
int getd(int a, int b) {
return abs(x[b] - x[a]); }
void DP() {
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= n; i++) //区间长度为1时
dp[i][i][0] = dp[i][i][1] = abs(x[i] - x[c]) * sump;
for (int l = 2; l <= n; l++) {
for (int i = 1; i + l - 1 <= n; i++) {
int j = i + l - 1;
dp[i][j][0] =
min(dp[i + 1][j][0] + (sump - (s[j] - s[i])) * getd(i, i + 1),
dp[i + 1][j][1] + (sump - (s[j] - s[i])) * getd(i, j));
// s[j] - s[i] 表示区间[i + 1, j]的功率和
dp[i][j][1] = min(
dp[i][j - 1][0] + (sump - (s[j - 1] - s[i - 1])) * getd(i, j),
dp[i][j - 1][1] + (sump - (s[j - 1] - s[i - 1])) * getd(j - 1, j));
}
}
cout << min(dp[1][n][0], dp[1][n][1]) << endl;
}
int main() {
cin >> n >> c;
for (int i = 1; i <= n; i++) {
cin >> x[i] >> w[i];
sump += w[i];
s[i] = s[i - 1] + w[i];
}
// s[i][j] = s[j] - s[i - 1];
DP();
system("pause");
return 0;
}