JZOJ 5453. 【NOIP2017提高A组冲刺11.5】好路线

58 篇文章 0 订阅
5 篇文章 0 订阅

Description

nodgd在旅游。现在,nodgd要从城市的西北角走到东南角去。这个城市的道路并不平坦,nodgd希望找出一条相对比较好走的路。
nodgd事先已经得到了这个城市的地图。地图上这个城市是一个n*m的矩形,nodgd现在站在坐标为(1,1)的位置,需要到达坐标为(n,m)的位置。这张地图上用非负整数标记了每个整数坐标点的海拔,坐标为(x,y)的位置的海拔是ℎ(x,y)。nodgd希望找出一条路线,路线中任意时刻都在向正东或向正南走,而且只在整数坐标点的地方转弯,使得路上经过的n+m−1个整数坐标点的海拔的方差最小。然而万能的nodgd当然知道该怎么走,也当然知道方差最小是多少,只是想顺便考考你。

Description

在本题中为了方便,你只需要求出(n+m−1)^2×σ^2的最小值即可,众所周知这是个整数。

Input

第一行输入两个整数n,m,表示城市的大小。
接下来n行,每行m个数,其中第x行第y个数就是ℎ(x,y)。

Output

输出一行一个整数,表示(n+m−1)^2×σ^2的最小值。

Sample Input

2 2
1 2
3 4

Sample Output

14

Data Constraint

对于30%的数据,1≤n,m≤10;
对于50%的数据,1≤n,m≤20;
对于100%的数据,1≤n,m≤50,0≤ℎ(x,y)≤50。

Hint

样例解释:有两条路1-2-4和1-3-4,方差都等于14/9,所以方差最小值是14/9,输出14。

Solution

  • 首先化简式子,设

    k=n+m1
    S=X1+X2++Xk

  • 则有:

    x=Sk

σ2=(X1Sk)2+(X2Sk)2++(XkSk)2k

  • 化简:

    σ2=(X2k+X22++X2k)S2kk

  • 所以就得到式子:

    k2σ2=k(X2k+X22++X2k)S2

  • 观察数据范围,搜索似乎不太可行,于是我们考虑DP。

  • F[i][j][S] 表示走到 (i,j) ,状态为 S 的最优值。

  • 发现设最优值不好转移,根据单调性可知可以改成最优值下的平方和,最后统计一遍答案即可。

  • 往下走则有:

    F[i+1][j][S+a[i+1][j]]=Min{F[i][j][S]+a[i+1][j]2}

  • 往右走同理,由于 h 值的限制,时间复杂度就是 O(N4) ,可以接受。

  • 总结

    1. 看到式子就想到化简;

    2. 看到方差就想到 平方和和的平方

    3. 平均数 可以用 总和 除以 总数 代替。

Code

#include<cstdio>
#include<cstring>
#define work(x,y) x=min(x,y)
using namespace std;
const int N=52,inf=1e9;
int n,m,num,mx,ans=inf;
int a[N][N],b[N][N],f[N][N][N*N<<1];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline int max(int x,int y)
{
    return x>y?x:y;
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
int main()
{
    n=read(),m=read(),num=n+m-1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            mx=max(mx,a[i][j]=read());
            b[i][j]=a[i][j]*a[i][j];
        }
    memset(f,60,sizeof(f));
    f[1][1][a[1][1]]=b[1][1];
    mx*=num;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            for(int k=0;k<=mx;k++)
            {
                if(i<n && k+a[i+1][j]<=mx) work(f[i+1][j][k+a[i+1][j]],f[i][j][k]+b[i+1][j]);
                if(j<m && k+a[i][j+1]<=mx) work(f[i][j+1][k+a[i][j+1]],f[i][j][k]+b[i][j+1]);
            }
    for(int k=0;k<=mx;k++)
        if(f[n][m][k]<inf) work(ans,num*f[n][m][k]-k*k);
    printf("%d",ans);
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值