P1006 [NOIP 2008 提高组] 传纸条

题目描述

小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排坐成一个 mmmnnn 列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标 (1,1)(1,1)(1,1),小轩坐在矩阵的右下角,坐标 (m,n)(m,n)(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。

在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。

还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用 000 表示),可以用一个 [0,100][0,100][0,100] 内的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。现在,请你帮助小渊和小轩找到这样的两条路径。

输入格式

第一行有两个用空格隔开的整数 mmmnnn,表示班里有 mmmnnn 列。

接下来的 mmm 行是一个 m×nm \times nm×n 的矩阵,矩阵中第 iiijjj 列的整数表示坐在第 iiijjj 列的学生的好心程度。每行的 nnn 个整数之间用空格隔开。

输出格式

输出文件共一行一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。

输入输出样例 #1

输入 #1

3 3
0 3 9
2 8 5
5 7 0

输出 #1

34

说明/提示

代码思路
初始化与输入处理
定义了一个四维数组dp[i][j][p][q],表示第一条路径走到(i,j),第二条路径走到(p,q)时的最大和。初始时,所有值设为极小值(0xaf),除了起点dp[1][1][1][1]=0。
输入矩阵数据存储到m[i][j]中。

动态规划状态转移
四重循环遍历所有可能的路径组合。对于每条路径的移动方向(右或下),状态转移方程为:

dp[i][j][p][q] = max(
    dp[i-1][j][p-1][q],  // 第一条向下,第二条向下
    dp[i][j-1][p][q-1],  // 第一条向右,第二条向右
    dp[i-1][j][p][q-1],  // 第一条向下,第二条向右
    dp[i][j-1][p-1][q]   // 第一条向右,第二条向下
) + m[i][j] + m[p][q];

注意条件q=j+1确保两条路径不会重复经过同一格子(起点和终点除外)。
输出结果
最终结果是 dp[n][m1-1][n-1][m1] ,表示第一条路径从(n,m1-1)向右,第二条路径从(n-1,m1)向下到达终点的最大和。

优化与改进
空间优化
四维数组可能占用较大空间,可优化为三维数组(利用步数同步的性质)。
代码可读性
添加注释说明状态定义和转移逻辑,便于理解。
边界条件
检查输入矩阵的行列是否合法,避免越界。
示例解释
假设矩阵为:

3 3
1 2 3
4 5 6
7 8 9

最优路径为:

路径1:(1,1)→(2,1)→(3,1)→(3,2)→(3,3),数值为1+4+7+8+9=29。
路径2:(1,1)→(1,2)→(1,3)→(2,3)→(3,3),数值为1+2+3+6+9=21。
总和为50,但代码逻辑需进一步验证是否符合实际题意。

【数据范围】

对于 30%30\%30% 的数据,满足 2≤m,n≤102 \le m,n \le 102m,n10
对于 100%100\%100% 的数据,满足 2≤m,n≤502 \le m,n \le 502m,n50

【题目来源】

NOIP 2008 提高组第三题。

#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=55;
int n,m1,dp[N][N][N][N],m[N][N];
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	memset(dp,0xaf,sizeof(dp));
	cin>>n>>m1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m1;j++)
			cin>>m[i][j];
	dp[1][1][1][1]=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m1;j++)
			for(int p=1;p<=n;p++)
				for(int q=j+1;q<=m1;q++)
					dp[i][j][p][q]=max(dp[i-1][j][p-1][q],max(dp[i][j-1][p][q-1],max(dp[i-1][j][p][q-1],dp[i][j-1][p-1][q])))+m[i][j]+m[p][q];
	cout<<dp[n][m1-1][n-1][m1];
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值