P1220 关路灯

//P1220 关路灯
//特色:一题多解
//https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P1220关于区间动态规划的初步证明,摘抄如下:
//很类似lrj的紫书《算法竞赛入门经典》上面一个题目(P293)
//首先我们发现当前时刻已经关闭的灯在一个连续区间里面最划算。这很显然:如果a和b是不连续的两盏灯,被灯c分开,那么我关了a再去关b的时候完全可以顺带把c关掉,答案只会更好。然后我们发现在关闭了一个连续的区间的灯之后,人一定在端点。
//于是就可以dp了,设f(i,j,k)f(i,j,k)f(i,j,k)为已经关闭了区间[i,j][i,j][i,j]里面的灯时的最小耗电量,k=0表示人在区间左端点,k=1表示人在区间右端点。
//于是f(i,j,0)f(i,j,0)f(i,j,0)就可以从f(i+1,j,0/1)f(i+1,j,0/1)f(i+1,j,0/1)转移而来。考虑从i+1或者j走到点i的路程中,还开着的灯的耗电即可。详见代码。
//http://blog.csdn.net/mrtintin/article/details/76293311此文写得不错,摘抄如下:
//用dp[i][j][0]表示关了[i,j]这个区间的灯,最后关的是第i盏;
//f[i][j][1]表示关了[i,j]这个区间的灯,最后关的是第j盏。
//sum[i]表示从1到i的灯的总功率。cal(i,j)表示除了[i,j]之外灯的总功率
//则有:
//dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][0]+(p[i+1]-p[i])*cal(i+1,j));
//dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][1]+(p[j]-p[i])*cal(i+1,j));
//dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][0]+(p[j]-p[i])*cal(i,j-1));
//dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][1]+(p[j]-p[j-1])*cal(i,j-1));
//http://blog.csdn.net/senyelicone/article/details/52403974此文也写得不错,摘抄如下:
//很久以前刚学动规的时候做过,实际上那时候连区间动规都不知道……现在再看感觉又不一样了啊……
//比较经典的区间动规吧,三个状态f[i][j][k]代表区间[i,j],k是方向,k=0,表示往i走;k=1,表示往j走~
//还有前缀和!
//初始化f的值不能太大!不然会爆掉!
//尝试用四种办法,一题多解,开先河之题,耗时两三天,但能举一反三,值。动态规划,f[i][j][] i<j;i>j两种;深度优先遍历,一种;广度优先遍历,一种。
//深搜,这篇代码写得不错,https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P1220 作者: 夏浅默 更新时间: 2015-08-22 10:31
//dfs是一个盏一盏的关灯
//编写完三种方法后,发现广度优先遍历,该题无法使用。2017-8-16 10:25 AC

//以下介绍三种方法,动态规划f[i][j][]i<j;动态规划f[i][j][]i>j;深度优先遍历dfs

方法一:动态规划f[i][j][]i<j

#include <stdio.h>
#include <string.h>
int f[50][50][2],pos[50],v[50];//f[i][j][0]最后关i f[i][j][1]最后关j 区间动态规划,i起点 j终点
int min(int a,int b){
    return a>b?b:a;
}
int main(){
    int n,c,i,j;
    v[0]=0;
    memset(f,127,sizeof(f));
    scanf("%d%d",&n,&c);
    for(i=1;i<=n;i++){
        scanf("%d%d",&pos[i],&v[i]);
        v[i]+=v[i-1];//v[]前缀和
    }
    //动态规划
    f[c][c][0]=f[c][c][1]=0;
    for(j=c;j<=n;j++)//处理过程中i<j
        for(i=j-1;i;i--){//1 此处写成 for(i=c-1;i;i--)
            f[i][j][0]=min(f[i+1][j][0]+(v[n]-(v[j]-v[i]))*(pos[i+1]-pos[i]),
            f[i+1][j][1]+(v[n]-(v[j]-v[i]))*(pos[j]-pos[i]));//f[i+1][j][0]落脚点在i+1 f[i+1][j][1]落脚点在j  a[j]-a[i]区间i-j内不包括i
            f[i][j][1]=min(f[i][j-1][1]+(v[n]-(v[j-1]-v[i-1]))*(pos[j]-pos[j-1]),
            f[i][j-1][0]+(v[n]-(v[j-1]-v[i-1]))*(pos[j]-pos[i]));//a[j-1]-a[i-1]区间i-j内不包括j f[i][j-1][1]落脚点在j-1 f[i][j-1][0]落脚点在i
        }
    printf("%d\n",min(f[1][n][0],f[1][n][1]));
    return 0;
}


方法二:动态规划f[i][j][]i>j

#include <stdio.h>
#define INF 999999
int f[60][60][2],pos[60],v[60];//2 int f[50][50][2],pos[50],v[50]; 测试点2 RE
int min(int a,int b){
    return a>b?b:a;
}
int main(){
    int n,c,i,j;
    scanf("%d%d",&n,&c);
    v[0]=0;
    for(i=1;i<=n;i++){
        scanf("%d%d",&pos[i],&v[i]);
        v[i]+=v[i-1];//前缀和
    }
    //1 memset(f,127,sizeof(f)) 测试点2WA 容易溢出
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){
            f[i][j][0]=INF;
            f[i][j][1]=INF;
        }
    f[c][c][0]=f[c][c][1]=0;
    for(j=c;j;j--)//i>j
        for(i=j+1;i<=n;i++){
            f[i][j][0]=min(f[i-1][j][0]+(v[n]-(v[i-1]-v[j-1]))*(pos[i]-pos[i-1]),
            f[i-1][j][1]+(v[n]-(v[i-1]-v[j-1]))*(pos[i]-pos[j]));
            f[i][j][1]=min(f[i][j+1][1]+(v[n]-(v[i]-v[j]))*(pos[j+1]-pos[j]),
            f[i][j+1][0]+(v[n]-(v[i]-v[j]))*(pos[i]-pos[j]));
        }
    printf("%d\n",min(f[n][1][0],f[n][1][1]));
    return 0;
}

方法三:深度优先遍历dfs

#include <stdio.h>
#include <string.h>
int pos[60],v[60],vis[60],n,ans=999999,tot=0;
void dfs(int x,int sum,int remain,int step){//x当前关掉灯的序号,sum当前消耗的能量, remain当前亮灯的功率, step当前关灯的盏数
    int left=x,right=x;
    if(sum>=ans)//剪枝
        return;
    if(step==n){
        ans=sum;
        return;
    }
    while(vis[left]&&left>=1)left--;//1.此处写成while(vis[x]&&left>=1);vis[left]=1表明灯已被关,被关的灯一定是连续的
    while(vis[right]&&right<=n)right++;//1.此处写成while(vis[x]&&right<=n)
    if(left>=1){
        vis[left]=1;
        dfs(left,sum+remain*(pos[x]-pos[left]),remain-v[left],step+1);
        vis[left]=0;
    }
    if(right<=n){
        vis[right]=1;
        dfs(right,sum+remain*(pos[right]-pos[x]),remain-v[right],step+1);
        vis[right]=0;
    }
}
int main(){
    int i,c;
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&c);
    for(i=1;i<=n;i++){
        scanf("%d%d",&pos[i],&v[i]);
        tot+=v[i];
    }
    vis[c]=1;
    dfs(c,0,tot-v[c],1);
    printf("%d\n",ans);
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值