wikioi1085 - 数字游戏(区间DP或者划分DP)

41 篇文章 0 订阅

题目链接 WIKIOI1085

【分析】

这题我知道的有两种方法:区间DP和划分DP,其实就是状态定义的不同。由于是环,只要把环打断变成链(扩大一倍)即可,然后每次枚举长度为n的段的起点就可以了,先说区间DP,就是用dp[i][j][m]表示区间i~j内划分成m份的最大价值,用sum[i][j]表示区间i~j中所有数字和%10;状态转移很简单只要把每个区间l~r分成不同的两段,两段最大价值相乘即可,也就是 dp[i][j][m] = max{ dp[i][k][m-1]*sum[k+1][r] };最小值则把max改成min在注意一下初始化就行,但是要注意转移的时候考虑不合法的状态不能转移进去,比如区间1~3如果还要分成4段那么是不可能的,所以不能转移进去。

但是,下载了所有测试数据,这题数据很弱,上面提到的不合法转移测试数据中没有考虑,导致有些程序明明错的也判成对的了

比如

3 3
3
3
3

应该输出

27
27

【AC代码(区间DP)】17ms

#include <cstdio>
#include <cstring>
#define MAXN 110
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
int a[MAXN], dp[MAXN][MAXN][10], sum[MAXN][MAXN], n;

int dfs(int l, int r, int m)
{
	int& ans = dp[l][r][m];
	if(~ans) return ans;
	if (1 == m || l >= r)
		return ans = sum[l][r];
	ans = 0;
	for (int k = m+l-2; k < r; k++)//r-l+1-m>=1 ==> r >= m+l-2防止区间l~r内个数小于划分数导致不合法sum[k+1][r]乘进去 
		ans = max(ans,dfs(l,k,m-1)*sum[k+1][r]);
	return ans;
}
int dfs2(int l, int r, int m)
{
	int& ans = dp[l][r][m];
	if(~ans) return ans;
	if (1 == m || l >= r)
		return ans = sum[l][r];
	ans = 0x3f3f3f3f;
	for (int k = m+l-2; k < r; k++)
		ans = min(ans,dfs(l,k,m-1)*sum[k+1][r]);
	return ans;
}
int main()
{
#ifdef SHY
	freopen("e:\\1.txt","r",stdin);
#endif
	int m;
	scanf("%d %d%*c", &n, &m);
	for (int i = 0; i < n; i++)
		scanf("%d%*c", &a[i]), a[i+n] = a[i];
	memset(sum,0,sizeof(sum));
	for (int i = 0; i < (n<<1); i++)
	{
		for (int j = i; j < (n<<1) && j < i+n; j++)
		{
			if (j)
				sum[i][j] = sum[i][j-1];
			sum[i][j] = ((sum[i][j]+a[j])%10+10)%10;
		}
	}
	int ans = 0x3f3f3f3f;
	memset(dp,-1,sizeof(dp));
	for (int i = 0; i < n; i++)
		ans = min(ans,dfs2(i,i+n-1,m));
	printf("%d\n", ans);
	ans = 0;
	memset(dp,-1,sizeof(dp));
	for (int i = 0; i < n; i++)
		ans = max(ans,dfs(i,i+n-1,m));
	printf("%d\n", ans);
	return 0;
}



再说另外一种方法:其实上面的L始终为0,可以把状态定义成为dp[r][m]表示区间1~r划分成m份的最大价值,这样就可以把转移方程改为dp[r][m] = max{ dp[k][m-1]*sum[k+1][r] };

还是枚举长度为n的段。不需要担心不合法状态了。由于状态中始终是从1开始的,所以需要每次计算sum[][]还有初始化dp[][]。这样其实只是降低了空间复杂度,时间复杂度是一样的

 

【AC代码(改进后)】15ms

#include <cstdio>
#include <cstring>
#define MAXN 52
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
int a[MAXN*2], dp[MAXN][10], sum[MAXN][MAXN], n;

int dfs(int r, int m)
{
	int& ans = dp[r][m];
	if(~ans) return ans;
	if (1 == m) return ans = sum[1][r];
	ans = 0;
	for (int k = 1; k < r; k++)
		ans = max(ans,dfs(k,m-1)*sum[k+1][r]);
	return ans;
}
int dfs2(int r, int m)
{
	int& ans = dp[r][m];
	if(~ans) return ans;
	if (1 == m) return ans = sum[1][r];
	ans = 0x3f3f3f;
	for (int k = 1; k < r; k++)
		ans = min(ans,dfs2(k,m-1)*sum[k+1][r]);
	return ans;
}
void c_sum(int x)//计算x~x+n的sum[][]
{
	for (int i = 1; i <= n; i++)
	{
		for (int j = i; j <= n; j++)
			sum[i][j] = ((sum[i][j-1]+a[j+x-1])%10+10)%10;
	}
}
int main()
{
#ifdef SHY
	freopen("e:\\1.txt","r",stdin);
#endif
	int m;
	scanf("%d %d%*c", &n, &m);
	for (int i = 1; i <= n; i++)
		scanf("%d%*c", &a[i]), a[i+n] = a[i];
	int ans = 0x3f3f3f3f;
	for (int i = 1; i <= n; i++)
	{
		c_sum(i);
		memset(dp,-1,sizeof(dp));
		ans = min(ans,dfs2(n,m));
	}
	printf("%d\n", ans);
	ans = 0;
	for (int i = 1; i <= n; i++)
	{
		c_sum(i);
		memset(dp,-1,sizeof(dp));
		ans = max(ans,dfs(n,m));
	}
	printf("%d\n", ans);
	return 0;
}


 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值