题目链接 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;
}