zoukankan      html  css  js  c++  java
  • 【题解】关路灯

    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)的枚举方式。

    反思。菜是原罪

  • 相关阅读:
    MYSQL索引
    MYSQL 外键 on语句 多表查询
    MYSQL用户管理
    MYSQL数据类型 表基本操作 表记录增删改 单表查询
    JQuery 引用方式
    JQuery 元素操作 each循环
    Android EditText方框验证码 短信验证码攻略
    android MVP模式介绍与实战
    一些不错的Android开源音视频播放器
    Android可伸缩布局-FlexboxLayout(支持RecyclerView集成)
  • 原文地址:https://www.cnblogs.com/h-lka/p/12623558.html
Copyright © 2011-2022 走看看