【HDU 6981】Rise in Price 搜索+剪枝

【HDU 6981】Rise in Price 搜索+剪枝

【题意】

​ 给出一个 n × n n\times n n×n 的矩阵,每个位置有两个权值 a i j , b i j a_{ij},b_{ij} aij,bij ,从 ( 1 , 1 ) (1,1) (1,1) 出发,每次只能向右或者向下,问走到 ( n , n ) (n,n) (n,n) 的所有路径中, ( ∑ a i j ) × ( ∑ b i j ) (\sum a_{ij})\times (\sum b_{ij}) (aij)×(bij) 最大可能是多少。

n ≤ 100 n\le 100 n100 保证 a i j , b i j a_{ij},b_{ij} aij,bij 是在 [ 1 , 1 0 6 ] [1,10^6] [1,106] 里均匀随机生成的。

【吐槽】

​ 比赛的时候没有想到标算的单调队列。

​ 但是发现题目说了数据是随机的,于是试了一下搜索加剪枝,试了两个估价函数之后就搜过了。

​ 而且貌似比其他队伍的标算都快(捂脸)

【分析】

​ 首先容易想到一个大暴力, d f s dfs dfs 枚举每条路径。

​ 然后思考最优性剪枝:如果在某个位置我们能估价未来的 ∑ a \sum a a ∑ b \sum b b ,并且估计的值的贡献大于实际能达到的贡献,就能用估计值去检验,如果估计值还达不到已经搜到过的最优解了,直接剪枝回溯。

​ 于是思考如何估价。

【估价函数1】

​ 假设当前已经经过路径的 ∑ a = S a \sum a = S_a a=Sa ,未来路径的 ∑ a = A \sum a =A a=A。已经经过路径的 ∑ b = S b \sum b = S_b b=Sb ,未来路径的 ∑ b = B \sum b =B b=B

​ 那么总贡献应该是 S a S b + A S b + B S a + A B S_aS_b+AS_b+BS_a+AB SaSb+ASb+BSa+AB

​ 如果我们用未来的 A m a x A_{max} Amax B m a x B_{max} Bmax (尽管这两个值几乎不可能出现在同一条路径方案上)来估计这个未来的总贡献,这个估计值一定是不低于实际值的,用来剪枝是合理的。

​ 所以分别预处理一下每个位置之后的路径的 A m a x A_{max} Amax B m a x B_{max} Bmax ,就可以 O ( 1 ) O(1) O(1) 计算估价函数。

​ 加上这个估价,大概能秒出 n = 30 n=30 n=30 的数据。

​ 但是过不了这个题。

【估价函数2】

​ 同样,假设当前已经经过路径的 ∑ a = S a \sum a = S_a a=Sa ,未来路径的 ∑ a = A \sum a =A a=A。已经经过路径的 ∑ b = S b \sum b = S_b b=Sb ,未来路径的 ∑ b = B \sum b =B b=B

​ 那么总贡献应该是 S a S b + A S b + B S a + A B S_aS_b+AS_b+BS_a+AB SaSb+ASb+BSa+AB

​ 上一个估价函数不太行,是因为估价中用到的 A m a x A_{max} Amax B m a x B_{max} Bmax 往往属于不同的路径,用它们估值,偏离实际情况较大。

​ 我们看看能不能用一条路径上的 A , B A,B A,B 值进行估价。

​ 我们令未来某条路径 i i i 的所有权值之和为 C i = A i + B i C_i=A_i+B_i Ci=Ai+Bi

​ 我们不妨找到 C m a x C_{max} Cmax 这是未来路径的双权值和的最大值。

​ 试想一下,如果我们可以自由分配未来路径上的 ∑ a i \sum a_i ai ∑ b i \sum b_i bi ,怎么样才能取得最优值?

​ 可以假设分配之后的 ∑ a i = A , ∑ b i = B \sum a_i = A,\sum b_i=B ai=A,bi=B A + B = C m a x A+B = C_{max} A+B=Cmax ,然后带入总贡献 S a S b + A S b + B S a + A B S_aS_b+AS_b+BS_a+AB SaSb+ASb+BSa+AB 计算,发现可以代出一个关于 B B B 的二次方程,找到当贡献最大时:
B = S a − S b + C m a x 2 A = S b − S a + C m a x 2 B= \frac{S_a-S_b+C_{max}}{2}\\ A= \frac{S_b-S_a+C_{max}}{2} B=2SaSb+CmaxA=2SbSa+Cmax
​ 那么我们就可以用这个 A , B A,B A,B 去估计未来值,而且这个估计值是我们在一条路径的限制之内调整到的最大值,因此也不低于实际值,用来剪枝是能保证正确性的。

​ 而且目测这个估值会比上个方案更接近实际情况一点。

​ 于是按这个写了一下,预处理出了每个位置的 C m a x C_{max} Cmax ,就能秒出 n = 100 n=100 n=100 的情况了,似乎踩了好多标算,至少在比赛里这个做法时间是最快的。

【代码】
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 101
#define LL long long
using namespace std;
int n,m;
int a[MAXN][MAXN],b[MAXN][MAXN];
int dpc[MAXN][MAXN];
LL ans=0;
void dfs(int x,int y,int suma,int sumb)
{
    if(x==n&&y==n)
    {
        ans=max(ans,1ll*suma*sumb);
        return;
    }
    if(x<n &&  1ll*(suma+dpc[x+1][y] - (suma-sumb+dpc[x+1][y])/2)*(sumb+(suma-sumb+dpc[x+1][y])/2) > ans)
        dfs(x+1,y,suma+a[x+1][y],sumb+b[x+1][y]);
    if(y<n &&  1ll*(suma+dpc[x][y+1] - (suma-sumb+dpc[x][y+1])/2)*(sumb+(suma-sumb+dpc[x][y+1])/2) > ans)
        dfs(x,y+1,suma+a[x][y+1],sumb+b[x][y+1]);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&a[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&b[i][j]);
        for(int i=n;i>=1;i--)
            for(int j=n;j>=1;j--)
            {
                dpc[i][j]=a[i][j]+b[i][j];
                if(i<n)
                    dpc[i][j]=max(dpc[i][j],a[i][j]+b[i][j]+dpc[i+1][j]);
                if(j<n)
                    dpc[i][j]=max(dpc[i][j],a[i][j]+b[i][j]+dpc[i][j+1]);
            }
        dfs(1,1,a[1][1],b[1][1]);
        cout<<ans<<endl;
    }
    return 0;
}
  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值