【题解】关路灯
反思一下没有想出来……思维固化了……
设\(dp[i][j][0/1]\)表示区间\(\text{[i,j]}\)全部关灯,且停止在\((i->0,j->1)\)位置的最小花费。
那么,\(dp[i][j][0]\)由\(dp[i+1][j][0/1]\)转移,\(dp[i][j][1]\)由\(dp[i][j-1][0/1]\)转移。
注意一下,我们求的是关上区间\(\text{[i,j]}\)的最小耗能。所以,我们每关上一盏灯,所用的时间要计算到其它所有没有关的灯上面去。
也就是说,每次要累计的答案,不仅仅是你关上这盏灯所要耗费的能量。
由于我们是没更新,或者说每关上一盏灯统计一下本次关灯耗费的时间所导致的消耗,所以不会造成算重。
接着注意到,这个方程,转移顺序是\(\text{i+1 to i,j-1 to j}\),所以枚举的时候\(i\)必然倒序,\(j\)必然顺序。
考虑怎么枚举。
显然我们已经知道的是\(C\)的答案,即老爷子的起始位置。所以我们的答案应该是基于这个位置去扩散的。
而且注意到\(i,j\)应该从\(C\)开始,所以,如果我们:
\(i=C \to 1,j=i \to n\)的话,我们的\(dp\)数组是可以得到正确更新的。
还有另一种方式:可以先枚举\(j\).
枚举:\(j=C \to n,i=j-1 \to 1\),同样解决问题。
于是,两份代码都可以喜提\(\color{green}{AC}\).
#include<bits/stdc++.h>
using namespace std;
int n,c;
int a[500],w[500],p[500];
int dp[51][51][2];
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
int main(){
scanf("%d%d",&n,&c);
for(int i=1;i<=n;++i){
scanf("%d%d",&a[i],&w[i]);
p[i]=p[i-1]+w[i];
}
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
dp[i][j][1]=dp[i][j][0]=99999;
dp[c][c][1]=dp[c][c][0]=0;
for(int i=c;i>=1;--i){
for(int j=i+1;j<=n;++j){
dp[i][j][0]=min(dp[i+1][j][0]+(a[i+1]-a[i])*(p[i]+p[n]-p[j]),
dp[i+1][j][1]+(a[j]-a[i])*(p[i]+p[n]-p[j]));
dp[i][j][1]=min(dp[i][j-1][1]+(a[j]-a[j-1])*(p[i-1]+p[n]-p[j-1]),
dp[i][j-1][0]+(a[j]-a[i])*(p[i-1]+p[n]-p[j-1]));
}
}
printf("%d\n",min(dp[1][n][1],dp[1][n][0]));
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int n,c;
int a[500],w[500],p[500];
int dp[51][51][2];
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
int main(){
scanf("%d%d",&n,&c);
for(int i=1;i<=n;++i){
scanf("%d%d",&a[i],&w[i]);
p[i]=p[i-1]+w[i];
}
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
dp[i][j][1]=dp[i][j][0]=99999;
dp[c][c][1]=dp[c][c][0]=0;
for(int j=c;j<=n;++j){
for(int i=j-1;i>=1;--i){
dp[i][j][0]=min(dp[i+1][j][0]+(a[i+1]-a[i])*(p[i]+p[n]-p[j]),
dp[i+1][j][1]+(a[j]-a[i])*(p[i]+p[n]-p[j]));
dp[i][j][1]=min(dp[i][j-1][1]+(a[j]-a[j-1])*(p[i-1]+p[n]-p[j-1]),
dp[i][j-1][0]+(a[j]-a[i])*(p[i-1]+p[n]-p[j-1]));
}
}
printf("%d\n",min(dp[1][n][1],dp[1][n][0]));
return 0;
}
两份代码的枚举顺序是不同的,但都过了。
总结:\(DP\)的时候,首先注意状态的设计,然后注意状态从哪里推出;再考虑这样\(DP\)的枚举方式。
反思。菜是原罪