noip2003 数字游戏 (区间动归)
转载
题目描述 Description
n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到一个数k。游戏的要求是使你所得的k最大或者最小。
n=4,m=2):
2
4 -1
3
((2-1) mod 10)×((4+3) mod 10)=1×7=7,要求最大值时,为((2+4+3) mod 10)×(-1 mod 10)=9×9=81。特别值得注意的是,无论是负数还是正数,对10取模的结果均为非负值。
丁丁请你编写程序帮他赢得这个游戏。
输入描述 Input Description
n(1≤n≤50)和m(1≤m≤9)。以下n行每行有个整数,其绝对值不大于104,按顺序给出圈中的数字,首尾相接。
输出描述 Output Description
输出文件有两行,各包含一个非负整数。第一行是你程序得到的最小值,第二行是最大值。
样例输入 Sample Input
4 2
4
3
-1
2
样例输出 Sample Output
7
81
数据范围及提示 Data Size & Hint
en
题目思路
此题是一道环形DP题,不是很难,将环形圈断开成单链,即将线性链条*2即可,具体如图所示
然后从第一个点1开始,向右方DP,求出以第一个点为起点的DP最大值,最小值,DP完成后再从第2个点2开始,求出以第2个点为起点的DP最大值、最小值......依次直到起点到达环形终点第五个点5,求出所有最大值中的最最大值,最小值中的最最小值,这里第i个点为起点的含义是第一个断开的地方是第i个点与第i-1个点之间。
下面是代码:
[cpp]
view plain
copy
print
?
1. #include <stdio.h>
2. #include <string.h>
3. #define MAX 10000000
4. #define MIN -10000000
5. int line[200],n,m,Min,Max,sum[200];
6. int f[200][20];//f[i][j]=第1-i个数分成j份,结果最大值
7. int g[200][20];//g[i][j]=第1-i个数分成j份,结果最小值
8. int min(int a,int b)
9. {
10. if(a>b)
11. return b;
12. return a;
13. }
14. int max(int a,int b)
15. {
16. if(a>b)
17. return a;
18. return b;
19. }
20. void dp(int a[])
21. {
22. int i,j,k;
23. for(i=1;i<=n;i++)
24. sum[i]=sum[i-1]+a[i];
25. for(i=0;i<=n;i++)
26. for(j=0;j<=m;j++)
27. {
28. f[i][j]=0;
29. g[i][j]=-1u>>1;
30. }
31. for(i=1;i<=n;i++)
32. {
33. f[i][1]=g[i][1]=(sum[i]%10+10)%10;
34. }
35. f[0][0]=1;
36. g[0][0]=1;
37. for(j=2;j<=m;j++)
38. {
39. for(i=j;i<=n;i++)
40. {
41. for(k=j-1;k<i;k++)
42. {
43. {
44. f[i][j]=max(f[i][j],f[k][j-1]*(((sum[i]-sum[k])%10+10)%10));
45. g[i][j]=min(g[i][j],g[k][j-1]*(((sum[i]-sum[k])%10+10)%10));
46. }
47. }
48. }
49. }
50. Max=max(Max,f[n][m]);
51. Min=min(Min,g[n][m]);
52. }
53. int main()
54. {
55. int i,j,k;
56. Max=0;
57. Min=-1u>>1;
58. "%d%d",&n,&m);
59. for(i=1;i<=n;i++)
60. {
61. "%d",&line[i]);
62. line[i+n]=line[i];
63. }
64. for(i=0;i<n;i++)
65. dp(line+i);
66. "%d\n%d\n",Min,Max);
67. return 0;
68. }