原文: https://blog.csdn.net/jacky_chenjp/article/details/63684427#%E9%A2%98%E7%9B%AE%E6%8F%8F%E8%BF%B0
分金子
题目描述: A、B两伙马贼意外地在一片沙漠中发现了一处金矿,双方都想独占金矿,但各自的实力都不足以吞下对方,经过谈判后,双方同意用一个公平的方式来处理这片金矿。 处理的规则如下:他们把整个金矿分成n段,由A、B开始轮流从最左端或最右端占据一段,直到分完为止。 马贼A想提前知道他们能分到多少金子,因此请你帮忙计算他们最后各自拥有多少金子?(两伙马贼均会采取对己方有利的策略) 输入: 测试数据包含多组输入数据。 输入数据的第一行为一个正整数T(T<=20),表示测试数据的组数。 然后是T组测试数据,每组测试数据的第一行包含一个整数n; 下一行包含n个数(n <= 500 ),表示每段金矿的含金量,保证其数值大小不超过1000。 输出: 对于每一组测试数据,输出一行"Case #id: sc1 sc2", 表示第id组数据时马贼A分到金子数量为sc1,马贼B分到金子数量为sc2。 详见样例。 样例输入: 2 6 4 7 2 9 5 2 10 140 649 340 982 105 86 56 610 340 879 样例输出: Case #1: 18 11 Case #2: 3206 981
用dp[ L ][ R ] 表示,从l 到 r 段,所能获得的最大值
则状态转移方程应该为 :
dp(L)(R) = (L至R总和) - min{ dp(L+1)(R), dp(L)(R-1) }
(这里的 dp(L+1)(R),和 dp(L)(R-1)指的是给对方留下多少,所以尽量给对方留下少的)
dp[L][R] L=R时,显然等于此处数值
用一个sum 数组记录从1 到 X 的总和,方便之后取各段总和
对 dp[i][i] 赋值 为对应下标数据, 含义为对长度为1的段赋值
之后开始循环,从dp[1][2]开始dp[2][3],dp[3][4] ... ...
长度由小到大,长度为2结束后,再循环长度为3的情况 dp[1][3],dp[2][4]......
直到最后一次长度为n , 此时求得 dp[1][N]
代码:
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int round = sc.nextInt();
for (int z = 0; z < round; z++) {
int n = sc.nextInt();
int[] a = new int[n + 1];
int[] sum = new int[n + 1];
int[][] dp = new int[n + 1][n + 1];
sum[0] = 0;
a[0] = 0;
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
sum[i] = sum[i - 1] + a[i];
dp[i][i] = a[i];
}
int k = 1;
while (k <= n - 1) {
for (int i = 1; i + k <= n; i++) {
dp[i][i + k] = sum[i + k] - sum[i - 1] -
Math.min(dp[i + 1][i + k], dp[i][i + k - 1]);
}
k++;
}
System.out.printf("Case #%d: %d %d\n" ,z+1,dp[1][n],sum[n]-dp[1][n]);
}
}
}
剪气球串
题目描述: 小明买了一些彩色的气球用绳子串在一条线上,想要装饰房间,每个气球都染上了一种颜色,每个气球的形状都是各不相同的。 我们用1到9一共9个数字表示不同的颜色,如12345则表示一串5个颜色各不相同的气球串。但小明希望得到不出现重复颜色的气球串,那么现在小明需要将这个气球串剪成多个较短的气球串,小明一共有多少种剪法?如原气球串12345的一种是剪法是剪成12和345两个气球串。 注意每种剪法需满足最后的子串中气球颜色各不相同(如果满足该条件,允许不剪,即保留原串)。两种剪法不同当且仅当存在一个位置,在一种剪法里剪开了,而在另一种中没剪开。详见样例分析。 输入: 第一行输入一个正整数n(1≤n≤100000),表示气球的数量。 第二行输入n个整数a1,a2,a3...an,ai表示该气球串上第i个气球的颜色。对于任意i,有1≤ai≤9。 输出: 输出一行,第一行输出一个整数,表示满足要求的剪法,输出最终结果除以1000000007后的余数。 样例输入: 3 1 2 3 样例输出: 4
思路:
dp[L]表示 L长度的时候有多少种剪法。
可以知道,如果没有重复的数字,dp[L] = dp[0]+dp[1]+...+dp[L-1] (要提前给dp[0]设置成1)
如果有重复的数字,设第一个重复数字从L向前数出现位置为X 则 dp[L] = dp[X] + dp[X+1] +...+dp[L-1]
(注意,这里重复的数字不单纯考虑第 L 位上的数字,而是整体数字串)
用一个十位的数组,每次记录是否数字已经出现过,没出现过则一直向前加和,出现了就停止。
(注意,这里加和时 加的是 dp[J-1],所以当重复数字出现时,该重复数字的 dp[X]已经加到dp[L]中了)
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
final int mod = 1000000007;
int n = sc.nextInt();
int[] a = new int[n+1];
int[] dp = new int[n+1];
int[] p = new int[10];
for(int i=1;i<=n;i++){
a[i]=sc.nextInt();
}
dp[0]=1;
for(int i=1;i<=n;i++){
int[] bo = new int[10];
int j = i;
while(j>0 && bo[a[j]]==0){
dp[i]+=dp[j-1];
bo[a[j]]=1;
dp[i]%=mod;
j--;
}
}
System.out.println(dp[n]);
}