[题解] cogs 2240 架设电话线路

http://cogs.pro:8080/cogs/problem/problem.php?pid=2240

与洛谷P2885几乎一致,https://www.luogu.org/problemnew/show/P2885,双倍经验。

定义dp[i][j]表示前i棵树的最大花费并且第i 棵树高度为j的。

我们可以想到这某一棵树的高度与它前边的树有直接的关系,不难有一种想法。

枚举第几棵树1->n

枚举每棵树的高度$h[i]-> max \{ h[1],h[2]...h[n] \} $

枚举第i棵树前边那棵树也就是第i-1棵树的高度。

现在我们想状态转移方程:

当第i棵数的高度为j时,那么需要花费$(j-h[i])^2$,与前边好要有连起来那就需要找到abs(j-dp[i-1][h[i]->maxh])中的最小值。

所以$dp[i][j]=min(dp[i][j],(j-h[i]^2+(j-dp[i-1][h[i]->maxh]) \times c))$.

那么算法时间复杂度为$O(n \times h \times h)$.

时间复杂度虽然比较高,奈何cogs数据比较水啊,勉强可以过。

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
int min(int a,int b){ return a<b?a:b; }
int n,a[100006],c,ans,f[100006][101];
inline int read()
{
    char c=getchar();
    int x=0;
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9')
      x=(x<<3)+(x<<1)+c-48,c=getchar();
    return x;
}
int main()
{
    freopen("phonewire.in","r",stdin);
    freopen("phonewire.out","w",stdout);
    ans=0x7fffffff;
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)
        for(int j=0;j<=100;j++)
            f[i][j]=1234567890;    
    for(int i=0;i<=100;i++)f[1][i]=(i-a[1])*(i-a[1]);
    for(int i=2;i<=n;i++)
    {
        for(int j=a[i];j<=100;j++)
        {
            for(int k=a[i-1];k<=100;k++)
            {
                f[i][j]=min(f[i][j],f[i-1][k]+(j-a[i])*(j-a[i])+abs(k-j)*c);
            }
        }
    }
    for(int i=1;i<=100;i++)ans=min(ans,f[n][i]);
    printf("%d",ans);
    fclose(stdin);fclose(stdout);
}

然而洛谷上不吸氧的话就需要优化一下了(以下话语来自洛谷管理---redbag)

不难发现,每次转移是个开口向上的二次函数(可以自己算算),然后我们枚举上一棵树的高度的过程中,

如果随着高度的增加费用增加了,就可以不用继续转移了。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    char s;
    int k=0,base=1;
    while((s=getchar())!='-'&&s!=EOF&&!(s>='0'&&s<='9'));
    if(s==EOF)exit(0);
    if(s=='-')base=-1,s=getchar();
    while(s>='0'&&s<='9')
    {
        k=k*10+(s-'0');
        s=getchar();
    }
    return k*base;
}
inline void write(int x)
{
    if(x<0)
    {
        putchar('-');
        write(-x);
    }
    else
    {
        if(x/10)write(x/10);
        putchar(x%10+'0');
    }
}
int n,c,p,x,s,mh;
int h[100100];
int f[100100][100];
int main()
{
    n=read();
    c=read();
    for (register int i=1; i<=n; i++)
    {
        h[i]=read();
        if (h[i]>mh) mh=h[i];
    }
    memset(f,1,sizeof(f));
    for (register int i=h[1]; i<=100; i++) f[1][i]=(i-h[1])*(i-h[1]);
    for (register int i=2; i<=n; i++)
    {
        for (register int j=h[i]; j<=mh; j++) //hm:电线杆的最大高度
        {
            s=(j-h[i])*(j-h[i]);//先算出来快些?
            p=233333333;
            for (register int k=h[i-1]; k<=mh; k++)
            {
                x=f[i-1][k]+s+c*abs(k-j);
                //下面和这一句是等效的,似乎快点?f[i][j]=min(f[i][j],x);
                if (x<f[i][j])
                {
                    f[i][j]=x;
                }
                if (x>p) break;//比上一个更多就不用转移了
                p=x;
            }
        }
    }
    int ans=f[n][h[n]];
    for (register int i=h[n]+1; i<=100; i++) ans=min(ans,f[n][i]); //找答案
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/rmy020718/p/9581584.html