【题解】关路灯

Question

反思一下没有想出来……思维固化了……

(dp[i][j][0/1])表示区间( ext{[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])转移。

注意一下,我们求的是关上区间( ext{[i,j]})的最小耗能。所以,我们每关上一盏灯,所用的时间要计算到其它所有没有关的灯上面去。

也就是说,每次要累计的答案,不仅仅是你关上这盏灯所要耗费的能量。

由于我们是没更新,或者说每关上一盏灯统计一下本次关灯耗费的时间所导致的消耗,所以不会造成算重。

接着注意到,这个方程,转移顺序是( ext{i+1 to i,j-1 to j}),所以枚举的时候(i)必然倒序,(j)必然顺序。

考虑怎么枚举。

显然我们已经知道的是(C)的答案,即老爷子的起始位置。所以我们的答案应该是基于这个位置去扩散的。

而且注意到(i,j)应该从(C)开始,所以,如果我们:

(i=C o 1,j=i o n)的话,我们的(dp)数组是可以得到正确更新的。

还有另一种方式:可以先枚举(j).

枚举:(j=C o n,i=j-1 o 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
",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
",min(dp[1][n][1],dp[1][n][0]));
	return 0;
}

两份代码的枚举顺序是不同的,但都过了。

总结:(DP)的时候,首先注意状态的设计,然后注意状态从哪里推出;再考虑这样(DP)的枚举方式。

反思。菜是原罪

原文地址:https://www.cnblogs.com/h-lka/p/12623558.html