P1220 关路灯——简单的区间dp
拿到题目之后想到可以用dp做
然后就开始乱想:
没什么意义的O(n^4) 解法
设
然后考虑状态转移的时候发现不对,需要记录关掉的最后一盏灯
接下来这个人又抽风了,之后想状态的时候脑子里想的变成更新到
于是为了状态转移,有多记录了一维时间
时间的最大值考虑最坏情况应当是
于是状态就变成了
但是这样空间复杂度达到了惊人的
考虑优化,可以发现关灯的区间必然是连续的,也就是说老张走到哪灯就会关到哪,否则必然有等效的更优解
所以说事实上
于是状态优化成了
考虑转移,对于左端点,只可能是由原本的区间向左拓展来的,也就是说只有两种情况,从那个区间的左/右端点跑过来
右端点同理
时间复杂度
AC Code:
#include <bits/stdc++.h>
using namespace std;
const int MAXN=55;
const int inf=0x3f3f3f3f;
int dp[MAXN][MAXN][MAXN*MAXN][2];//t时刻,关完[i,j]内的所有灯,最后在左/右端点时更新到的区域内的的最小耗电量
int p[MAXN],a[MAXN];
int n,c;
int dis(int x,int y){
return abs(a[x]-a[y]);
}//
int res=inf;
void work(){
//input...
scanf("%d%d",&n,&c);
for(int i=1;i<=n;++i)
scanf("%d%d",&a[i],&p[i]);
memset(dp,inf,sizeof(dp));
dp[c][c][0][0]=dp[c][c][0][1]=0;
for(int t=0;t<=n*n;++t){
for(int i=c+1;i<=n;++i)
if(t-dis(i,i-1)>=0)
dp[c][i][t][1]=dp[c][i-1][t-dis(i-1,i)][1]+t*p[i];
for(int i=c-1;i>=1;--i)
if(t-dis(i+1,i)>=0)
dp[i][c][t][0]=dp[i+1][c][t-dis(i+1,i)][1]+t*p[i];
for(int r=c+1;r<=n;++r)
for(int l=c-1;l>=1;--l){
if(t-dis(r,r-1)>=0)
dp[l][r][t][1]=min(dp[l][r][t][1],dp[l][r-1][t-dis(r,r-1)][1]+t*p[r]);
if(t-dis(r,l)>=0)
dp[l][r][t][1]=min(dp[l][r][t][1],dp[l][r-1][t-dis(r,l)][0]+t*p[r]);
}
for(int l=c-1;l>=1;--l)
for(int r=c+1;r<=n;++r){
if(t-dis(l,l+1)>=0)
dp[l][r][t][0]=min(dp[l][r][t][0],dp[l+1][r][t-dis(l,l+1)][0]+t*p[l]);
if(t-dis(r,l)>=0)
dp[l][r][t][0]=min(dp[l][r][t][0],dp[l+1][r][t-dis(r,l)][1]+t*p[l]);
}
for(int r=1;r<=n;++r)
for(int l=1;l<=r;++l){
if(t-dis(r,r-1)>=0)
dp[l][r][t][1]=min(dp[l][r][t][1],dp[l][r-1][t-dis(r,r-1)][1]+t*p[r]);
if(t-dis(r,l)>=0)
dp[l][r][t][1]=min(dp[l][r][t][1],dp[l][r-1][t-dis(r,l)][0]+t*p[r]);
if(t-dis(r,l)>=0)
dp[l][r][t][0]=min(dp[l][r][t][0],dp[l+1][r][t-dis(r,l)][1]+t*p[l]);
if(t-dis(l+1,l)>=0)
dp[l][r][t][0]=min(dp[l][r][t][0],dp[l+1][r][t-dis(l+1,l)][0]+t*p[l]);
}
res=min(res,min(dp[1][n][t][0],dp[1][n][t][1]));
}//
printf("%d",res);
}
int main(){
work();
return 0;
}
O(n^2) 正解
事实上之前搞成那个鬼样子都是因为中途把自己设计的状态搞错了
事实上如果是维护全局的耗电量的话,是不需要维护时间维的
那末状态就可爱多了,
状态转移方程差不多,直接看代码吧
要注意的是这里要用前缀和来算区间总功率
AC Code:
#include <bits/stdc++.h>
using namespace std;
const int MAXN=55;
const int inf=0x3f3f3f3f;
int a[MAXN],sum[MAXN];
int dp[MAXN][MAXN][2];
int n,c;
int dis(int x,int y){
return abs(a[x]-a[y]);
}//
int query(int l,int r){
return sum[r]-sum[l-1];
}//
void work(){
scanf("%d%d",&n,&c);
int p,r;
for(int i=1;i<=n;++i){
scanf("%d%d",&a[i],&p);
sum[i]=sum[i-1]+p;
}
memset(dp,inf,sizeof(dp));
dp[c][c][0]=dp[c][c][1]=0;
for(int len=2;len<=n;++len){
for(int l=1;l+len-1<=n;++l){
r=l+len-1;
dp[l][r][0]=min(
dp[l+1][r][0]+dis(l+1,l)*(query(1,n)-query(l+1,r)),//左端点跑过来
dp[l+1][r][1]+dis(l,r)*(query(1,n)-query(l+1,r))); //右端点跑过来
dp[l][r][1]=min(
dp[l][r-1][1]+dis(r-1,r)*(query(1,n)-query(l,r-1)),//右端点跑过来
dp[l][r-1][0]+dis(l,r)*(query(1,n)-query(l,r-1))); //左端点跑过来
}
}
printf("%d",min(dp[1][n][1],dp[1][n][0]));
}//
int main(){
work();
return 0;
}