[POJ3612]telephone wire

来源:互联网 发布:淘宝考试以下不是催情 编辑:程序博客网 时间:2024/06/05 07:47

dp

我们设f[i][j]表示当考虑了前i座塔 第i座塔高度为j的最小花费

初始全部∞

我们有f[i][j]=min(f[i][j],f[i-1][k]+(j-a[i])^2+abs(j-k)*c)

这样我们枚举i j k时间复杂度nc²

然后只好考虑优化了

f[i][j]=min(f[i-1][k]-k*c)+j*c+(j-a[i])^2(j>=k)

f[i][j]=min(f[i-1][k]+k*c)-j*c+(j-a[i])^2(j<k)

你发现了什么……?对 min里面的式子可以直接O(c)求

这样我们将一个O(c²)的式子拆成了两个O(c)求的式子

时间复杂度O(n*2c) 空间复杂度O(n*c)

这样已经可过掉本题了

#include<cstdio>#include<cstdlib>#include<iostream>#include<algorithm>#include<cstring>#include<cmath>using namespace std;const int INF = 0x7f7f7f7f;int dp[100001][101];int high[101],low[101];int n,c;int now[100001];inline int init(){int now=0;char c;bool flag=false;while(1){c=getchar();if(c>='0'&&c<='9'){now=now*10+c-'0';flag=true;}else if(flag)return now;}}int main(){n=init();c=init();for(int i=1;i<=n;i++){now[i]=init();for(int j=1;j<=100;j++){dp[i][j]=INF;}}dp[1][now[1]]=0;for(int i=now[1];i<=100;i++){dp[1][i]=pow((i-now[1]),2);}for(int i=2;i<=n;i++){low[0]=INF;high[0]=INF;high[101]=INF;for(int j=1;j<=100;j++){low[j]=min(dp[i-1][j]-j*c,low[j-1]);}for(int j=100;j>=1;j--){high[j]=min(dp[i-1][j]+j*c,high[j+1]);}for(int j=now[i];j<=100;j++){int p=pow(j-now[i],2);dp[i][j]=min(low[j]+(j*c)+p,p+high[j]-(j*c));}}int ans=INF;for(int i=1;i<=100;i++){ans=min(ans,dp[n][i]);}printf("%d",ans);return 0;}
但是其实我们还能做到更好

考虑优化空间复杂度

我们发现每次转移只和上次的状态有关系

那么滚动数组

#include<cstdio>#include<cstdlib>#include<iostream>#include<algorithm>#include<cstring>#include<cmath>using namespace std;const int INF = 0x7f7f7f7f;int dp[101];int high[101],low[101];int n,c;int now[100001];inline int init(){int now=0;char c;bool flag=false;while(1){c=getchar();if(c>='0'&&c<='9'){now=now*10+c-'0';flag=true;}else if(flag)return now;}}int main(){n=init();c=init();for(int i=1;i<=n;i++){now[i]=init();}for(int i=1;i<=100;i++){dp[i]=INF;}dp[now[1]]=0;for(int i=now[1];i<=100;i++){dp[i]=pow((i-now[1]),2);}for(int i=2;i<=n;i++){low[0]=INF;high[0]=INF;high[101]=INF;for(int j=1;j<=100;j++){low[j]=min(dp[j]-j*c,low[j-1]);}for(int j=100;j>=1;j--){high[j]=min(dp[j]+j*c,high[j+1]);dp[j]=INF;}for(int j=now[i];j<=100;j++){int p=pow(j-now[i],2);dp[j]=min(low[j]+(j*c)+p,p+high[j]-(j*c));}}int ans=INF;for(int i=1;i<=100;i++){ans=min(ans,dp[i]);}printf("%d",ans);return 0;}

不过挖个小坑 也希望看到这篇博文的人能帮我一下

1、求high函数时 为什么要从上往下?

好吧我自己找到答案了

low[j]表示的貌似是高度为j时从下往上的最小的花费

high[j]表示的貌似是高度为j时从上往下的最小的花费

这样low[j],high[j]肯定是类似一个区间的东西 表示的是区间内最小值 这样每次转移一定没问题

感谢fyj404的答案

首先 我们要确定一个事实 每次上边j转移都是从1->j和j->c转移的

我们用low和high实际上维护的是1->j的最小值和j->c的最小值 也就是我们要转移的东西

2、使用滚动数组时为什么一定要置为∞?(我觉得不变也可以吧 因为每次转移的状态应该只和上次的部分状态有关系)

这个问题还不确定

0 0