洛谷 P1220 关路灯

先附上我最开始的想法 记忆化搜索

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,i,ans;
int sum1[55],sum2[55],s[55],p[55];
int jiyi[55][55][2];
int dp(int a,int b,int z){
    int fanhui=jiyi[a][b][z];
    if(fanhui!=0){
        return fanhui;
    }else{
        if(a==1&&b==n){
            fanhui=0;
        }
        else{
            if(a==1){
                if(z==0){
                    fanhui=dp(a,b+1,1)+sum2[b+1]*(s[b+1]-s[a]);
                }
                else{
                    fanhui=dp(a,b+1,1)+sum2[b+1]*(s[b+1]-s[b]);
                }
            }
            else{
                if(b==n){
                    if(z==0){
                        fanhui=dp(a-1,b,0)+sum1[a-1]*(s[a]-s[a-1]);
                    }else{
                        fanhui=dp(a-1,b,0)+sum1[a-1]*(s[b]-s[a-1]);
                    }
                }
                else{
                    if(z==0){
                        fanhui=dp(a-1,b,0)+(sum1[a-1]+sum2[b+1])*(s[a]-s[a-1]);
                        fanhui=min(fanhui,dp(a,b+1,1)+(sum1[a-1]+sum2[b+1])*(s[b+1]-s[a]));
                    }
                    else{
                        fanhui=dp(a-1,b,0)+(sum1[a-1]+sum2[b+1])*(s[b]-s[a-1]);
                        fanhui=min(fanhui,dp(a,b+1,1)+(sum1[a-1]+sum2[b+1])*(s[b+1]-s[b]));
                    }
                }
             }
        }
    }
    return fanhui;
}
int main(){
    cin>>n>>m;
    for(i=1;i<=n;i++){
        cin>>s[i];
        cin>>p[i];
    }
    sum1[0]=0;
    sum1[n+1]=0;
    for(i=1;i<=n;i++){
        sum1[i]=sum1[i-1]+p[i];
    }
    for(i=n;i>=1;i--){
        sum2[i]=sum2[i+1]+p[i];
    }
    ans=dp(m,m,0);
    cout<<ans;
    return 0;
}

这个程序本身是可以运行的 但是时间复杂度不允许,在洛谷数据中TLE了5个点

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,v[55],ans=0x7fffffff;
struct sty{
    int x,w;
    }a[55];
  //当前位置,已经关了的点,当前时间,当前总耗电,当前没关的灯的总功率。
int min(int a,int b){
    if(a>b) return b;
    return a;
}
void dfs(int now,int num,int t,int sum,int su)
{
    if(sum+su*t>=ans) return;
    if(num==n-1)
    {
    ans=min(ans,sum);
    return;
    }
    for(int i=1;i<=n;++i){
        if(now+i<=n&&!v[now+i]){
            int t1=fabs(a[now].x-a[now+i].x);
            v[now+i]=1;
            dfs(now+i,num+1,t+t1,sum+a[now+i].w*(t+t1),su-a[now+i].w);
            v[now+i]=0;
            break;
        }
    }
    for(int i=-1;i>=-n;--i)
        if(now+i>=1&&!v[now+i]){
            int t2=fabs(a[now].x-a[now+i].x);
            v[now+i]=1;
            dfs(now+i,num+1,t+t2,sum+a[now+i].w*(t+t2),su-a[now+i].w);
            v[now+i]=0;
            break;
        }
}

int main()
{
    int su=0;
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        scanf("%d%d",&a[i].x,&a[i].w);
        if(i!=m) su+=a[i].w;///统计除了出生点以外的所有点的总功率
    }
    v[m]=1;
    dfs(m,0,0,0,su);
    cout<<ans<<endl;
    return 0;
}

这一套是经过剪枝后的记忆化搜索,

第一次加的判定是if(sum>ans)return ;这个很好理解,就是当前耗电大于答案,剪掉。

第二次是if(sum+(当前已用的时间)*(当前没关的灯的总功率)>ans)return ;

但仍然考试中TLE五个点= =


然而这题的正统方法是区间DP

先附上AC代码:

#include <cstdio>
#include <cstring>
using namespace std;
const int MAXM=60;
int a[MAXM],b[MAXM],sum[MAXM],n,m,c;
int f[MAXM][MAXM][2];
int min(int a,int b
{return a<b?a:b;}
int max(int a,int b)
{return a>b?a:b;}
int main()
{
  scanf("%d%d",&n,&c);
  memset(f,127,sizeof(f));//赋成极大值防止后面的min出问题
  for(int i=1;i<=n;i++)
    scanf("%d%d",&a[i],&b[i]),sum[i]=sum[i-1]+b[i];
  f[c][c][0]=f[c][c][1]=0;//瞬间被关(初始化)
  for(int l=2;l<=n;l++)
    for(int i=1;i+l-1<=n;i++)
      {
    int j=i+l-1;
    f[i][j][0]=min(f[i+1][j][0]+(a[i+1]-a[i])*(sum[i]+sum[n]-sum[j]),//继续走下去是否更快
               f[i+1][j][1]+(a[j]-a[i])*(sum[i]+sum[n]-sum[j]));//从j点折返回来是否更快(此时假设[i+1][j]被关,i亮,从j端点往回赶去关i)
//要注意的一点是sum[n]-(sum[j]-sum[i])是包括了i这一点的电能的,因为走过来的过程中灯i也会耗电
    f[i][j][1]=min(f[i][j-1][0]+(a[j]-a[i])*(sum[i-1]+sum[n]-sum[j-1]),//同上
               f[i][j-1][1]+(a[j]-a[j-1])*(sum[i-1]+sum[n]-sum[j-1]));
        }
  int ans=min(f[1][n][0],f[1][n][1]);
  printf("%d",ans);
  return 0;
}

其中f【i】【j】【0/1】是指当前关闭i-j的灯并且站在了i(三维数是0)或j(三维数是1)

因为数据范围很和谐,所以三维轻松AC

自定义min max是因为害怕会因为这一点点的时间而TLE

原文地址:https://www.cnblogs.com/SKTskyking/p/12774624.html