HDU6981

题目大意

​ 给定一个\(n*n\)的方阵,从\((1,1)\)开始走向\((n,n)\),从\((i,j)\)可到达\((i+1,j)\),\((i,j+1)\)。每到达一个位置,可获得位置上的\(a_{ij}\)个宝石,并使所有宝石单价上升\(b_{ij}\)。求出走到终点后卖出宝石的总收益(最初宝石单价为\(0\))。

​ 我们可以确定的是,除了位于第\(1\)行或第\(1\)列的位置,每个位置都可从其上方或是左方转移过来。换而言之,一个位置大概率会存不止一个方案。我们所要做的,就是将所有方案剔除一部分,然后将不好直接判断的方案进行暴力比对。

​ 一个方案由两个参数组成——宝石数量和宝石单价。我们难以直接判断,但在假设宝石数量递增的前提下,若是新加入的方案满足宝石单价大于原先方案,换而言之就是新方案的宝石单价,宝石数目都大于旧方案,那显然旧方案就毫无存在的必要了。某种程度上来说这也是一种单调队列的思想——数目比你多,单价还比你高,那显然会更优。

​ 关于复杂度,显然遍历每个位置为\(O(n^2)\)的复杂度,但对于队列中元素个数并不明确,取决于有序元素个数...复杂度应该是\(O(kn^2)\)这样....\(k\)的大小不太会证明,如有\(dalao\)清楚怎么证明,还请教教我\(qwq\)。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string.h>
#include <cmath>
#include <deque>
#include <vector>
#define Lint long long
using namespace std;
const int N = 105;
int T, n, a[N][N], b[N][N],m;
struct node
{
    int x, y;
};
node q[10000005];
vector<node> f[N][N];
void Insert(node x){
    while(m&&x.y>=q[m].y){
        m--;
    }
    if(!m||q[m].x<=x.x){
        q[++m]=x;
    }
}
void work(vector<node> x,vector<node> y,vector<node> &z){
    int i=0,j=0;
    m=0;
    while(i<x.size()&&j<y.size()){
        if(x[i].x<y[j].x){
            Insert(x[i++]);
        }
        else{
            Insert(y[j++]);
        }
    }
    while(i<x.size()) Insert(x[i++]);
    while(j<y.size()) Insert(y[j++]);
    for(int i=1;i<=m;++i){
        z.push_back(node{q[i].x,q[i].y});
    }
}
int main()
{
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= n; ++j)
            {
                scanf("%d", &a[i][j]);
                f[i][j].clear();
            }
        }
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= n; ++j)
            {
                scanf("%d", &b[i][j]);
            }
        }
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= n; ++j)
            {
                if(i==1&&j==1){
                    f[i][j].push_back(node{a[i][j],b[i][j]});
                    continue;
                }
                if(i==1){
                    f[i][j]=f[i][j-1];
                }
                else if(j==1){
                    f[i][j]=f[i-1][j];
                }
                else{
                    work(f[i-1][j],f[i][j-1],f[i][j]);
                }
                for(int k=0;k<f[i][j].size();++k){
                    f[i][j][k].x+=a[i][j];
                    f[i][j][k].y+=b[i][j];
                }
            }
        }
        Lint ans=-1;
        for(auto k:f[n][n]){
            ans=max(ans,1ll*k.x*1ll*k.y);
        }
        printf("%lld\n",ans);
    }
    return 0;
}