【考题题解8】两次动归较简单题题解

1.跑步HLOJ#1161


详细见代码:

#include<bits/stdc++.h>
using namespace std;
int f[20000][600]={};
int main()
{
	int n,m,a[10000]={};
	cin>>n>>m;
	for (int i=1;i<=n;i++)
	    cin>>a[i];
	for (int i=1;i<=n;i++)
	{
		f[i][0]=f[i-1][0];
		for (int j=1;j<=m;j++)
		{
		    f[i][j]=max(f[i][j],f[i-1][j-1]+a[i]);
		    if (i-j>=0) f[i][0]=max(f[i-j][j],f[i][0]);
		}
	}
	cout<<f[n][0];
}

2.渡河问题#HLOJ1162

可以根据公交乘车的思路进行理解

我们设f[i]为前i头牛渡河的最大值,当我们进行状态转移的时候就可以这样:

枚举j(1~i-1),表示不和第i头牛一起过河的数量总和,然后:

1.f[i]的初值,t[i](时间前缀和),表示第i头牛单独过河

2.进行状态转移是的状态转移方程是f[i]=max(f[j]+t[i-j]+m),表示的意思是j头牛单独过河的最小值加上剩下牛过河的最小中加上送药河岸的时间

3.值得注意的是,每次必须要加上返回的时间。

#include<bits/stdc++.h>
using namespace std;
int f[20000][600]={};
int main()
{
	int n,m,a[10000]={},f[10000]={},t[10000]={};
	cin>>n>>m;
	for (int i=1;i<=n;i++)
	    cin>>a[i];
	for (int i=1;i<=n;i++)
	    t[i]=t[i-1]+a[i];
	f[1]=a[1]+m;
	for (int i=2;i<=n;i++)
	{
		f[i]=t[i];
		for (int j=1;j<=i-1;j++)
		    f[i]=min(f[i],f[j]+t[i-j]+m);
		f[i]+=m;
	}
	cout<<f[n];
	return 0;
}

3.马拉松HLOJ1164

我们设f[i][j]表示能够走到第i个点跳过j个点所能走到的最小体力

我们可以枚举i和j(这个已经简单到不用再解释为什么了吧)

然后枚举l,表示连续的跳l个到i点的数目

然后:

f[i][j]=min(f[i][j],f[i-l-1][j-l]+dis(i-l-1,i));

表示从i-l-1跳到第i格的距离加上从起点跳到第i-l-1格的距离和

然后就可以出程序了:


#include<bits/stdc++.h>
using namespace std;
struct p {int x,y;}a[10000]={};
int dis(int l,int r){return abs(a[l].x-a[r].x)+abs(a[l].y-a[r].y);}
int main()
{
	int n,k,f[600][600];
	cin>>n>>k;
	for (int i=1;i<=n;i++)
	   cin>>a[i].x>>a[i].y;
	memset(f,99,sizeof(f));
	for (int i=1;i<=n;i++)
	    f[i][i]=1;
	f[1][0]=0;
	for (int i=2;i<=n;i++)
	    for (int j=0;j<=min(i-2,k);j++)//跳跃的次数 
	        for (int l=0;l<=j;l++)//连续跳跃的点
			    f[i][j]=min(f[i][j],f[i-l-1][j-l]+dis(i-l-1,i));
	cout<<f[n][k];
	return 0;
}

4.养我(猪)HLOJ#1165

我道题有一个可怕而无法解决的无后效性

然后可以用pi进行排序:即下降的慢的后面杀,这样的话DP过程中就可以保证当前状态是最优的

接下来,我们设f[i][j]为前i头猪杀j头猪的最优值,在这里,我们不展开详细讨论。

接下来推荐一下hzk大佬的一段话:

大概是这样的一道题:

有人要卖猪,开始一天第i头猪的价值为ai,但每天第i头猪的价值会减少pi,若这头猪已经价值为负数了,那么这头猪的价值会变为0.

那么每天只能卖一头猪,问你k天后的最大价值.

那么这是一道DP题.

怎么DP,这明显有后效性.

那我们对它进行一个排序.

这个排序的准则是什么?

我们可以把这几头猪按照pi从大到小排序.

那么后面的猪一定是在前面的猪后面杀的.

证明的话可以用反证法:

假设猪i在猪j后面杀(i<j,已经排序过了),那么pi一定小于pj,肯定不会比猪i在前面杀划算.

若会到0,你就不用管了.

那么我们的排序准则就是以pi为第一关键字,ai为第二关键字排序.

所以我们的后效性问题解决了,我们就可以开始DP了.

我们设dp[i][j]表示前i头猪杀了j头的最大利润.

那么很明显我们可以列出方程:f[i][j]=max(f[i-1][j],f[i-1][j-1]+add).

其中add=max(0,a[i]-p[i]*(j-1)).

参见代码:

#include<bits/stdc++.h>
using namespace std;
struct Pig{int begin,pi;}a[10000]={};
inline bool pigcmp(Pig x,Pig y){return x.pi>y.pi;}
int f[1000][1000]={};
int main()
{
	int n,p,maxx=-1;
	cin>>n>>p;
	for (int i=1;i<=n;i++)
	    cin>>a[i].begin;
	for (int i=1;i<=n;i++)
	    cin>>a[i].pi;
	sort(a+1,a+n+1,pigcmp); 
	for (int i=1;i<=n;i++)
	    for (int j=1;j<=i&&j<=p;j++)
	    {
	        f[i][j]=max(f[i-1][j]/*不取*/,max(0/*<=*/,f[i-1][j-1]+a[i].begin-a[i].pi*(j-1)/*取*/));
	        maxx=max(maxx,f[i][j]);
        }
    cout<<maxx;
	return 0;
}



猜你喜欢

转载自blog.csdn.net/Ronaldo7_ZYB/article/details/79978208