Rise in Price

HDU6981

题目描述

题目给出一个 \(n*n\) 的网格,每个网格有一定数量的钻石和单价,要求你从 \((1,1)\) 的位置走到 \((n,n)\),只能向下或者向右走,同时每个网格有两个参数 \(a,b\),当你走到这个网格时,你的钻石数量会增加 \(a\),你的钻石单价会增加 \(b\),现在让你输出走到终点时可以得到的最大的钻石总价值(数量✖️ 单价)。
大致是这么个意思

解题思路

首先可以想到动态规划来解决。

\[dp[i] [j] = max(dp[i-1][j],dp[i] [j-1]) \]

但是这个题目要求的是乘积最大化,所以不能直接维护单个变量,应该同时维护的单价和数量,所以我们对于每个位置维护一个可能成为答案的序列,即:(单价,数量)。

\[dp[i] [j] = dp[i-1] [j] + dp[i] [j-1] \]

每次转移后需要将 \((i,j)\)位置的序列都加上当前位置的参数 \(a,b\)
但是暴力继承是无法接受的,所以需要在继承的时候删去一些对答案没有贡献的值,考虑什么样的值一定不会成为答案,就是当某一个状态的单价和数量都比另一个状态的单价和数量要小或者等时,这个状态就不可能成为答案。
所以在这里我们只需要把每个位置的序列维护成单调栈即可,即对于 \((i,j)\) 位置,在转移时:

\((i-1,j)\)\((i,j-1)\)位置的序列组合成一个优先第一维递增,其次第二维递减的序列
然后将处理出来的序列跑一遍单调栈,当当前元素的两个参数都比栈顶元素的两个参数大或者等时,就弹出栈顶元素
最后将当前元素入栈。

这样就删去了一些不必要的状态。

Code

#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
#define V vector
using namespace std;
const int N = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int a[107][107],b[107][107];
V<PII>v[107][107];
void dp(V<PII>&x,V<PII>&y,V<PII>&z){
	V<PII>vv;
	int i = 0, j = 0, len1 = x.size(), len2 = y.size();
	while(1){
		if(i == len1 && j == len2)break;
		if(i == len1){
			vv.pb(y[j++]);
		}else if(j == len2){
			vv.pb(x[i++]);
		}else{
			if(x[i].fi < y[j].fi)vv.pb(x[i++]);
			else if(x[i].fi > y[j].fi)vv.pb(y[j++]);
			else if(x[i].se > y[j].se)vv.pb(x[i++]);
			else vv.pb(y[j++]);
		}
	}
	z.clear();
	V<PII>st(vv.size() + 2);
	int len = 0;
	for(PII &p : vv){
		while(len && p.fi >= st[len].fi && p.se >= st[len].se){
			len--;
		}
		st[++len] = p;
	} 
	for(i = 1; i <= len; i++){
		z.pb(st[i]);
	}
}
void solve(){
	int n;
	cin >> n;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			cin >> a[i][j];
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			cin >> b[i][j];
	v[1][1].clear();
	v[1][1].pb({a[1][1],b[1][1]});
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++){
			if(i == j && i == 1)continue;
			if(i == 1)v[i][j] = v[i][j-1];
			else if(j == 1)v[i][j] = v[i-1][j];
			else{
				dp(v[i-1][j],v[i][j-1],v[i][j]);
			}
			for(PII &x : v[i][j]){
				x.fi += a[i][j];
				x.se += b[i][j];
			}
		}
	ll ans = 0; 
	for(PII x : v[n][n])
		ans = max(ans,(ll)x.fi*x.se);
	cout << ans << "\n";
}

int main()
{
	#ifdef ONLINE_JUDGE
	#else
		freopen("in.txt", "r", stdin);
		freopen("out.txt", "w", stdout);
	#endif

	qc;
	int T = 1;
	cin >> T;
	while(T--){
		solve();
	}
	return 0;
}
Code will change the world !