Luogu P1220 关路灯(区间DP)

题目链接:https://www.luogu.com.cn/problem/P1220

题意:给定n个灯,最开始村长在第s个路灯处。

然后给定每个路灯的位置ai和每秒钟消耗的电量bi。(ai递增)

(1<=s<=n<=50)。

最开始关上s灯,不费时间。问从关s灯开始到关完所有灯最少花费多少时间。

题解:dp[i][j][0/1]分别表示关掉i~j之间的灯后留在i还是j(每种情况又有两种转移状态,比如i可能是由i+1或者j转移而来)。然后枚举区间即可。

另外还需要注意的是,需要+sum[i][j](前面消耗时间累积的消耗电量,至于操作,想一想也不难)。

代码:

struct XXX {
	ll n,s,a[maxn],b[maxn],pre[maxn];
	//pre为前缀和
	ll sum[maxn][maxn],dp[maxn][maxn][2];
	//sum[i][j]表示已经关掉i~j之间灯后剩余的灯每s 消耗的总电量。
	//dp[i][j][0],dp[i][j][1]分别表示关掉i,j之间的灯最后停在i还是j
	//每种情况又分别i+1/j->i;j-1/i->j。就是这两种情况做转移状态
	void init() {
		read(n),read(s);
		for(ll i=1; i<=n; i++) read(a[i]),read(b[i]),pre[i]=pre[i-1]+b[i];
	}
	void init1() {
		for(ll i=1; i<=n; i++)
			for(ll j=1; j<=n; j++)
				sum[i][j]=pre[n]-(pre[j]-pre[i-1]),dp[i][j][0]=dp[i][j][1]=inf;
//		dp[s][s][0]=dp[s][s][1]=0;
	}
	ll solve() {
		init1();
		//枚举可能的区间
		for(ll i=s; i>=1; i--) {
			for(ll j=s; j<=n; j++) {
				if(i==j) {
					dp[i][j][0]=dp[i][j][1]=0;
					continue;
				}
				dp[i][j][0]=min(dp[i+1][j][0]+(a[i+1]-a[i])*sum[i+1][j],
				                dp[i+1][j][1]+(a[j]-a[i])*sum[i+1][j]);//注意这里的*sum,是不停处理叠加的时间*每颗灯的消耗
				dp[i][j][1]=min(dp[i][j-1][1]+(a[j]-a[j-1])*sum[i][j-1],
				                dp[i][j-1][0]+(a[j]-a[i])*sum[i][j-1]);
			}
		}
		return min(dp[1][n][0],dp[1][n][1]);
	}
	void write() {
		print(solve(),' ');
	}
} X;
int main() {
	X.init();
	X.solve();
	X.write();
	return 0;
}

 

你可能感兴趣的:(#,ACM_动态规划DP,DP,区间)