最高积分问题题解

目录

原题:

【题目描述】

【输入格式】

【输出格式】

【样例输入】

【样例输出】

题目大意:

主要思路:

状态的表示:

状态的转移:

最后我们还要记录一下是从哪转移的:

注意事项:

代码:



原题:

【题目描述】

鱼大大带领的学霸队现在要面对一个挑战,鱼大大希望他的队员们最后能拿到最高分。

鱼大大有N个队员,编号为1~N,每位队员各有擅长。本次挑战中有M个小任务,其编号顺序为1~M。本次挑战的规则是每位队员按编号顺序先后选择一个小任务解决(且只能选择一个),并且后选择任务的队员不能选择比之前队员选择的任务序号更小的任务解决。即假设1号队员选定了3号任务,那么2号队员只能选择3号往后的任务。

由于每个任务要求不相同,每位队员擅长的也不相同,因此不同的任务被不同的队员解决后的积分也是不同的。(当然没有队员解决某一任务时,任务积分为0)

这里用一个表格表示不同任务被不同队员解决后的积分:

任务1

任务2

任务3

任务4

任务5

队员1

7

23

-5

-24

16

队员2

5

21

-4

10

23

队员3

-21

5

-4

-20

20

表格说明:例如队员1解决擅长的任务2,能拿到23分,但若是解决不擅长的任务4反而会倒扣24分

领队鱼大大为了拿到最高积分,不打乱队员编号顺序的情况下,应该如何安排队员面对挑战?如果能拿到最高积分的方案不止一种,输出其中任意一种方案。

【输入格式】

第一行包含两个数:N,M

随后的N行中,每行包含M个整数,Aij即为第i位队员解决第j个任务能获得的积分

【输出格式】

第一行一个整数表示能获得的最高积分。

第二行N个整数表示每个队员解决的任务编号

【样例输入】

3 5

7 23 -5 -24 16

5 21 -4 10 23

-21 5 -4 -20 20

【样例输出】

 53

2 4 5

注:样例说明:

最高积分为53,

安排方案为:

队员1解决任务2,获得23分

队员2解决任务4,获得10分

队员3解决任务5,获得20分

数据范围:

N为队员的数量,队员编号从1至N,M是任务的数量。

Aij是队员i解决任务j时的获得的积分。

前70%数据:1≤N≤M≤100,-50≤Aij≤50

后30%数据:2000≤N≤M≤3000,-1000≤Aij≤1000

题目大意:

给了我们一个n×m的矩阵,然后让我们在每一行选一个数,这个数的列位置要大于上一行选的数的列位置。图例:

求所有选的数的和的最大值。

主要思路:

这个题一看就知道是用dp做的,既然是dp,我们就要考虑表示和转移。

状态的表示:

这个dp题和之前普通的dp都不一样。这个dp两维都要表示前几。于是我们可以这样定义状态。

要用两维表示

dp[i][j]:代表从前i行前j列选的数的和的最大值。

状态的转移:

我们可以从两个方面来想:

  1. 可以从上一行转移过来。
  2. 可以从前面转移过来。

对于第一个方面,我们可以轻而易举的列出转移方程:dp[i-1][j-1]。i-1是上一行,j-1是因为要大于上一行选的列位置。还要加上这个任务的分数。所以是dp[i-1][j-1]+a[i][j]

对于第二个方面:我们可以这样想,如果这一行前面的最大值会比第一个方面的最大值大,那我们就可以继承那个最大值。

最后我们还要记录一下是从哪转移的:

我们可以开一个数组path数组记录是从哪转移过来的,对于第一个方面:就是从j转移过来的,就是path[i][j] = j,对于第二个方面,就是从前面最大值那里转移过来的,path[i][j] = path[i][j-1]

注意事项:

此题数据范围较大,要用IO优化。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[3010][3010]; 
int dp[3010][3010];
int path[3010][3010];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	memset(dp,-0x3f,sizeof(dp));
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>a[i][j];
		}
	}
	for(int i=0;i<=m;i++)
	{
		dp[0][i] = 0;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			dp[i][j] = dp[i][j-1];
			path[i][j] = path[i][j-1];
			if(dp[i][j]<dp[i-1][j-1]+a[i][j])
			{
				dp[i][j] = dp[i-1][j-1]+a[i][j];
				path[i][j] = j;
			}
		}
	}
	int c = path[n][m];
	vector<int> v;
	for(int i=n;i>=1;i--)
	{
		v.push_back(c);
		c = path[i-1][c-1];
	}
	cout<<dp[n][m]<<'\n';
	reverse(v.begin(),v.end());
	for(int i=0;i<v.size();i++)
	{
		cout<<v[i]<<' ';
	}
	return 0;
}

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值