636程设训练1

https://vjudge.net/contest/230123

题目1 POJ - 2676:

数独题,暴力搜索就好,但是要注意记录当前每一行,每一列哪些用过,哪些没用过,这样复杂度是n^2才不会超时,否则每一层递归都要重新判断一下是否可以填某个数就很容易超时。这相当于是一种记忆化的方法,需要掌握。

题目2 POJ - 2159:

密码题,这个比较简单,就是把每种字母重复的次数找出来,排序然后比较一下两个序列是否相等就好了。

题目3 HDU - 1024:

Max Sum Plus Plus,非常值得分析。相当于是一个对给定顺序的序列分段的题目,需要求每一段和的最大和。这种分段的题目一般考虑最后一个取和不取两种情况。dp[i][j]表示前j个分成i段最大值,并且最后一段取到j。这样的话,dp[i][j]=max(dp[i][j-1]+num[j],max(dp[i-1][k],i-1<=k<=j-1)+num[j]),相当于两种情况前j-1个分成i段,j-1取到,最后一段再加一个j;然后还可以是前k个分成i-1份,第j个单独成一段。但是要注意的是dp[m][n]不是最终的答案,因为不知道最大值时最后一段的最后一个数到底在哪。

这道题目只是这样做还是过不了,因为数据非常大。还需要运用滚动数组,用一维数组不断滚动刷新来代替二维数组。同时还要用一个数组来记录前k个分成i-1段的最大值(注意不一定要取到k,所以就是ma[i-1][k]=max(dp[i-1][k],ma[i-1][k-1]))当然这个数组也不能是二维的,这样也会爆内存,所以也要用滚动刷新数组的方法来实现。要特别注意数组刷新的时机,不要犯错,可以在草稿纸上先写下二维数组,然后看当时的i到底是多少来判断什么时候刷新。最后还有一点就是dp数组的初始化,在本题中初始化为0即可。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string> 
#include <iostream>
#include <string.h>
using namespace std;
#define MAX 1000005
#define INF 0x7fffffff
int num[MAX];
int dp[MAX];
int m,n;
int ma[MAX];
int mmax;
int main()
{
	while(~scanf("%d%d",&m,&n))
	{
		for(int i=1;i<=n;++i)
		{
			scanf("%d",&num[i]);
		}
		memset(dp,0,sizeof(dp));memset(ma,0,sizeof(ma));
		for(int i=1;i<=m;++i)
		{
			mmax=-INF;
			for(int j=i;j<=n;++j)
			{
				dp[j]=max(dp[j-1]+num[j],ma[j-1]+num[j]);
				ma[j-1]=mmax;
				mmax=max(dp[j],mmax);
			}
		}
		cout<<mmax<<endl;
	}
	return 0;
}

题目4 OpenJ_Bailian - 4150

上机,本题代码量很小但是思维比较困难。首先分析的是每个人获得的能力值只和左右相邻两个位置先后有关,所以只需要局部考虑三个相邻位子的情况即可。相邻三个座位的先后情况共有6种,分为三类

1)中间最先到2 1 3 ,3 1 2

2)中间第二个到1 2 3 ①,3 2 1 ②

3)中间最后到1 3 2 ,2 3 1

因为我们是++i这样往后考虑所以只需要关心每一种情况中中间位和左边位的先后关系。所以可以用dp[i][0]表示1)情况时前i个位子能力值的最大值,dp[i][1]表示2)的①时前i个位子能力值的最大值,dp[i][2]表示2)的②时前i个位子能力值的最大值,dp[i][3]表示3) 时前i个位子能力值的最大值。只需要考虑每种情况对应的前面一组可能的情况即可。最后注意dp数组的初始化。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string> 
#include <iostream>
#include <string.h>
using namespace std;
const int MAX=10005,INF=0x7fffffff;
int n;
int a[MAX],b[MAX],c[MAX];
int dp[MAX][4];
int main()
{
	cin>>n;
	for(int i=1;i<=n;++i)cin>>a[i];
	for(int i=1;i<=n;++i)cin>>b[i];
	for(int i=1;i<=n;++i)cin>>c[i];
	dp[1][0]=a[1];dp[1][2]=b[1];dp[1][1]=-INF;dp[1][3]=-INF;
	for(int i=2;i<=n;++i)
	{
		dp[i][0]=max(dp[i-1][2],dp[i-1][3])+a[i];
		dp[i][1]=max(dp[i-1][0],dp[i-1][1])+b[i];
		dp[i][2]=max(dp[i-1][2],dp[i-1][3])+b[i];
		dp[i][3]=max(dp[i-1][1],dp[i-1][0])+c[i];
	}
	cout<<max(dp[n][0],dp[n][1]);
	return 0;
}

本题还有一种解法就是将问题看成在两个位子之间插入不等号的子问题,dp[i][0]表示在i之前插入<后1~i-1之间能力值的最大值;dp[i][1]表示在i之前插入>后1~i-1之间能力值的最大值。第一个和最后一个需要特判。同时n=1的情况也需要特判。

if (n == 1) {
cout << a[1] << endl;
return 0;
}
dp[2][0]=a[1];dp[2][1]=b[1];
for(int i=3;i<=n;++i){
	dp[i][0]=max(dp[i-1][1]+a[i-1],dp[i-1][0]+b[i-1]);
	dp[i][1]=max(dp[i-1][1]+b[i-1],dp[i-1][0]+c[i-1]);
}
cout<<max(dp[n][0]+b[n],dp[n][1]+a[n]);

猜你喜欢

转载自blog.csdn.net/yhjpku/article/details/80544033