Maja

本文探讨了一个关于Maja授粉路径的算法问题,Maja在一个由花朵组成的网格上授粉,目标是在限定步数内从蜂巢出发并返回,最大化授粉花朵数量。文章介绍了解题思路,包括使用动态规划方法,特别关注于路径特性,如原路返回和路径重复模式,最终提供了一个高效的算法实现。

题目

题目描述
MajaMajaMaja和蜜蜂在一片神奇的草地上为花授粉,这块草地可以表示为一个n(n≤100)n(n\le 100)n(n100)m(m≤100)m(m\le 100)m(m100)列的矩形,在第iii行第jjj列中有Cij(Cij≤1000000000)C_{ij}(C_{ij}\le 1000000000)Cij(Cij1000000000)朵没有授粉的花。

MajaMajaMaja的蜂巢位于第a(1≤a≤n)a(1\le a\le n)a(1an)行第b(1≤b≤n)b(1\le b\le n)b(1bn)列,她将从她的蜂巢开始为这些花授粉,去草地上的某些块授粉,然后再返回她的蜂巢。每次操作,MajaMajaMaja可以向相邻的上下左右中的一个方格移动,而且她永远不会离开草地。每次她经过的某块草地,都会给这块草地上所有未授粉的花授粉。但草地很神奇,一旦MajaMajaMaja离开草地(i,j)(i,j)(i,j),所有授粉的花都会消失,新的未授粉的花又会在这片土地上生长。

由于MajaMajaMaja不可能永远飞行,她会在飞过k(2≤k≤1000000000)k(2\le k\le 1000000000)k(2k1000000000)个格子后感到疲倦,并乐意向她的蜜蜂朋友们讲述她的冒险故事。请问,在MajaMajaMaja授粉并在kkk步后返回蜂巢,她能授粉的花的数量是多少?

输出输出格式
我不想打 见标程

思路

要是你不认真想正解 也能想到就算我输 ,你可以想到一个BFS(BFS(BFS(或者dp)dp)dp)的做法——存一个位置+++步数的状态。没错,它是O(nmk)O(nmk)O(nmk)的!相当的快呢!

好,接下来想正解:

  • 起点和终点重合的路径,一定是“原路返回”!

为什么呢?因为走到 一半(没错就是恰好一半,毕竟你要走偶数步)的时候,你有两种选择:一:原路返回;二:接着走。你来的时候走哪条,哪条路的权值就大,回去的时候就该走哪条路。

  • 路径中最多只会在两个点之间来回移动!

为什么呢?因为在x(x>2)x(x>2)x(x>2)个点(设为p1,p2,p3,...,pxp_1,p_2,p_3,...,p_xp1,p2,p3,...,px)之间来回移动,等同于从p1p_1p1出发走一步(走到p2p_2p2)回来+++p2p_2p2出发走一步(走到p3p_3p3)回来+...++...++...+px−1p_{x-1}px1出发走一步回来。倒不如直接找p1,p2,p3,...,px−1p_1,p_2,p_3,...,p_{x-1}p1,p2,p3,...,px1中出发走一步回来的值最大的一个,不断的这么走呢。

现在,凭借上面的结论,你已经知道路径长什么样子了:在这里插入图片描述
后面那个黑坨坨表示反反复复 老不好,快用……

为什么不是中间的某一个区间反反复复,必须是头头上反反复复呢?

因为从中间反反复复,就会违背第一个结论了!

代码

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

const int MAXN = 100,dis[][2] = {1,0,0,1,-1,0,0,-1};
const long long longInf = (1ll<<62)-1;
long long dp[2][MAXN][MAXN];// dp[s,x,y] 走2s步走到(x,y)最大的权值 
// s进行滚动,权值忽略反反复复 
int a[MAXN][MAXN];

int main(){
	int n, m, k, x, y;
	scanf("%d %d %d %d %d",&n,&m,&x,&y,&k); k >>= 1;
	for(int i=0; i<n; ++i) for(int j=0; j<m; ++j)
		scanf("%d",&a[i][j]);
	for(int i=0; i<n; ++i) for(int j=0; j<m; ++j)
		dp[0][i][j] = -longInf;
	dp[0][x-1][y-1] = 0;
	long long ans=0;
	for(int step=1; step<=k and step<n*m; ++step){
		for(int i=0; i<n; ++i) for(int j=0; j<m; ++j){
			long long tmp = -longInf, d = -longInf;
			for(int dir=0,tox,toy; dir<4; ++dir){
				tox = i+dis[dir][0]; toy = j+dis[dir][1];
				if(tox < 0 or tox == n or toy < 0 or toy == m)
					continue;
				tmp = max(tmp,dp[(step&1)^1][tox][toy]+a[tox][toy]);
// 走到了(tox,toy)然后又走了一步走到(i,j);a[i][j]在最后加 
				d = max(d,0ll+a[tox][toy]);
// 考虑最后反反复复的地方,肯定是a[i][j]+a[tox][toy] 
// 同样的,d中的a[i][j]也是最后加 
			}
			dp[step&1][i][j] = tmp+a[i][j];
			if(dp[step&1][i][j] < 0) continue; // 是-longInf 
			ans = max(ans,dp[step&1][i][j]+(k-step)*(d+a[i][j]));
		}
	}
	printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值