bzoj1084(dp)

分了两种情况,

m==1时,dp即可,关键是要注意负数的处理,有负数的时候,初始化为0就很可能有bug


m==2时,想了一个dp,然而有一种情况没有考虑,codevs数据真是良心,给我留了90分,先贴自己的代码,然后重新写一下

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int inf=0x3f3f3f;
int f[109][109][3][5],a[109][3],n,m,k,dp[109][12];
int work1()
{
	int f[109][120][2],a[109];
	memset(f,-inf,sizeof(f));
	memset(a,0,sizeof(a));
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	f[1][1][1]=a[1];f[1][0][0]=0;
	for (int i=2;i<=n;i++)
	{
		f[i][0][0]=0;
		for (int j=1;j<=min(k,i);j++) 
		{
			f[i][j][1]=max(f[i-1][j][1]+a[i],max(f[i-1][j-1][0]+a[i],f[i-1][j-1][1]+a[i]));
			f[i][j][0]=max(f[i-1][j][1],f[i-1][j][0]);
		}
	}
	return max(f[n][k][1],f[n][k][0]);//当m==1时,情况处理 
}
int max(int a,int b,int c,int d)
{
	return max(max(a,b),max(c,d));
}
int work2()//m==2时 
{
	for (int i=1;i<=n;i++) scanf("%d%d",&a[i][1],&a[i][2]),a[i][0]=a[i][1]+a[i][2];
	memset(f,-inf,sizeof(f));
	for (int kk=0;kk<3;kk++)
	{
		for (int i=n;i>=1;i--)
		{
			f[i][i][kk][3]=f[i][i][kk][4]=a[i][kk];
			f[i][i][kk][0]=f[i][i][kk][1]=f[i][i][kk][2]=0;
			for (int j=i+1;j<=n;j++)
			{
				f[i][j][kk][0]=
				max(f[i][j-1][kk][0],f[i][j-1][kk][1],f[i+1][j][kk][2],f[i+1][j][kk][0]);
				f[i][j][kk][1]=a[j][kk]+f[i][j-1][kk][1];
				f[i][j][kk][2]=a[i][kk]+f[i+1][j][kk][2];
				f[i][j][kk][3]=
				max(f[i][j-1][kk][3]+a[j][kk],f[i+1][j][kk][3]+a[i][kk]);
				f[i][j][kk][4]=max(f[i][j][kk][1],f[i][j][kk][2],f[i][j][kk][3],f[i][j][kk][0]);
			}
		}
	}//处理区间i~j,的最大值
	/*for (int i=1;i<=n;i++)
		for (int j=i;j<=n;j++)
		printf("%d %d %d\n",i,j,f[i][j][2][4]);*/
	memset(dp,-inf,sizeof(dp));
	dp[0][0]=0;
	for (int i=1;i<=n;i++)
	{
		dp[i][0]=0;
		for (int j=1;j<=min(i,k);j++)
		{
			for (int l=1;l<=i;l++) 
			{
 				dp[i][j]=
	max(dp[i][j],dp[l-1][j-1]+f[l][i][0][4],dp[l-1][j-1]+f[l][i][1][4],dp[l-1][j-1]+f[l][i][2][4]);
				if (j>1) dp[i][j]=max(dp[i][j],dp[l-1][j-2]+f[l][i][1][4]+f[l][i][2][4]);
			}
		}
	}	
	return dp[n][k];
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	if (m==1) printf("%d",work1());
	else printf("%d",work2());
	return 0;
}

标准做法

f[i][j][k]表示第一列前i个数字,第二列前j个数字选出k个子矩阵的最大分值

转移还是O(N)

f[i][j][k] = max(f[i - 1][j][k], f[i][j - 1][k]);//不选这个点

如果选

f[i][j][k] = max{ f[i][j][k], f[x][j][k - 1] + s1[i] - s1[x] };//从左列枚举点,更新 

f[i][j][k] = max{ f[i][j][k], f[i][y][k - 1] + s2[j] - s2[y] };//从右列枚举,更新

当 i = j 时 f[i][j][k] = max{ f[i][j][k], f[x][x][k - 1] + s1[i] - s1[x] + s2[i] - s2[x] };//如果他们是相平的,就可以枚举两行的情况两了


#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int inf=0x3f3f3f;
int f[109][109][12],a[109][3],n,m,k,dp[109][12],sum[109][3];
int work1()
{
	int f[109][120][2],a[109];
	memset(f,-inf,sizeof(f));
	memset(a,0,sizeof(a));
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	f[1][1][1]=a[1];f[1][0][0]=0;
	for (int i=2;i<=n;i++)
	{
		f[i][0][0]=0;
		for (int j=1;j<=min(k,i);j++) 
		{
			f[i][j][1]=max(f[i-1][j][1]+a[i],max(f[i-1][j-1][0]+a[i],f[i-1][j-1][1]+a[i]));
			f[i][j][0]=max(f[i-1][j][1],f[i-1][j][0]);
		}
	}
	return max(f[n][k][1],f[n][k][0]);//当m==1时,情况处理 
}
int work2()//m==2时 
{
	for (int i=1;i<=n;i++) scanf("%d%d",&a[i][1],&a[i][2]);
	for (int i=1;i<=n;i++) sum[i][1]=sum[i-1][1]+a[i][1],sum[i][2]=sum[i-1][2]+a[i][2];
	
	for (int l=1;l<=k;l++)
		for (int a1=0;a1<=n;a1++)
			for (int a2=0;a2<=n;a2++)
			{
				if (a1+a2<l) continue;
				if (a1+a2==l)  
				{
					f[a1][a2][l]=sum[a1][1]+sum[a2][2];
					continue;
				}
				if (a1)
				{
					f[a1][a2][l]=f[a1-1][a2][l];
					for (int j=0;j<a1;j++)
					f[a1][a2][l]=max(f[a1][a2][l],f[j][a2][l-1]+sum[a1][1]-sum[j][1]);
				}
				if (a2)
				{
					f[a1][a2][l]=max(f[a1][a2-1][l],f[a1][a2][l]);
					for (int j=0;j<a2;j++)
					f[a1][a2][l]=max(f[a1][a2][l],f[a1][j][l-1]+sum[a2][2]-sum[j][2]);
				}
				if (a1==a2)
				{
					for (int j=0;j<a1;j++)
					f[a1][a2][l]=
					max(f[a1][a2][l],f[j][j][l-1]+sum[a1][1]-sum[j][1]+sum[a2][2]-sum[j][2]);
				}
			 } 
	return f[n][n][k];		 
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	if (m==1) printf("%d",work1());
	else printf("%d",work2());
	return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值