我一直试图在面试街上解决以下问题。计分记分卡(30分)
在锦标赛中,N名选手只相互比赛一次。每场比赛都会导致任何一名球员获胜。没有关系。你已经给出了一张记分卡,其中包含了比赛结束时每位球员的得分。玩家的得分是玩家在锦标赛中赢得的总游戏数。但是,某些玩家的得分可能已从记分卡中删除。有多少可能的记分卡与输入记分卡一致?
输入: 第一行包含案例数T.T案例如下。每个案例包含第一行的数字N,后面是第二行的N个数字。第i个数字表示s_i,即第i个玩家的得分。如果第i个玩家的得分已被删除,则表示为-1。
输出: 输出T行,包含每种情况的答案。输出每个结果模数为1000000007。
Constraints:
1 <= T <= 20
1 <= N <= 40
-1 <= s_i < N
Sample Input:
5
3
-1 -1 2
3
-1 -1 -1
4
0 1 2 3
2
1 1
4
-1 -1 -1 2
Sample Output:
2
7
1
0
12
说明: 对于第一种情况,可能有2个记分卡:0,1,2或1,0,2。 对于第二种情况,有效记分卡为1,1,1,0,1,2,0,2,1,0,0,2,1,2,0,2,0,1,2,1,0 。 对于第三种情况,唯一有效的记分卡是{0,1,2,3}。 对于第四种情况,没有有效的记分卡。两位球员都无法获得1分。
我试图提出通用函数方法,但我真的试图使用动态编程来解决这个问题。你怎么能想到这个问题的复发关系?。
答案 0 :(得分:1)
以下是针对上述问题的DP解决方案
public static int[][] table; // stores the result of the overlapping sub problems
private static int N;
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
int testCases = scanner.nextInt();
for (int i = 0; i < testCases; i++) {
N = scanner.nextInt();
int[] scores = new int[N];
for (int j = 0; j < N; j++) {
scores[j] = scanner.nextInt();
}
long result = process(scores) % 1000000007L;
System.out.println(result );
}
}
private static long process(int[] scores) {
int sum = 0;
int amongPlayers = 0; //count no of players whose score has been erased(-1)
for (int i = 0; i < N; i++) {
if (scores[i] != -1) {
sum += scores[i];
} else {
amongPlayers++;
}
}
int noGames = (N * (N -1)) /2; // total number of games
if (sum < noGames) {
int distribute = noGames - sum; // score needed to be distributed;
table = new int[distribute + 1 ][amongPlayers + 1];
for (int m = 0; m <= distribute; m++) {
for (int n = 0; n <= amongPlayers; n++) {
table[m][n] = -1;
}
}
return distribute(distribute, amongPlayers); // distrubute scores among players whose score is erased(-1)
}
else if(sum == noGames){
return 1;
}
return 0;
}
/**
* Dynamic programming recursive calls
* @param distribute
* @param amongPlayers
* @return
*/
private static int distribute(int distribute, int amongPlayers) {
if(distribute == 0 && amongPlayers == 0)
return 1;
if (amongPlayers <= 0)
return 0;
if(distribute == 0)
return 1;
int result = 0;
if (table[distribute][amongPlayers - 1] == -1) {
int zeroResult = distribute(distribute, amongPlayers - 1);
table[distribute][amongPlayers - 1] = zeroResult;
}
result += table[distribute][amongPlayers - 1];
for (int i = 1; i < N ; i++) { // A person could win maximum of N-1 games
if (distribute - i >= 0) {
if (table[distribute - i][amongPlayers - 1] == -1) {
int localResult = distribute(distribute - i,
amongPlayers - 1);
table[distribute - i][amongPlayers - 1] = localResult;
}
result += table[distribute - i][amongPlayers - 1];
}
}
return result;
}
答案 1 :(得分:1)
观察:
序列s [1],s [2],...,s [n]是一致的记分卡,这些属性必须保持:
首先,我们需要检查未删除的分数,只需使用1个条件。然后使用动态编程来删除分数。
让我们表示删除分数b [i],而不是删除分数a [i];
sum {i = 1 .. l} a [i] + sum {i = 1 ... k} b [i]&gt; =(k + l)*(k + l - 1)/ 2
sum {i = 1 .. l} a [i] + sum {i = 1 .. k} b [i]&gt; = 0 + 1 + .. +(k + l - 1)
sum {i = 1 .. l}(a [i] - (k + i - 1))+ sum {i = 1 ... k} b [i]&gt; = 0 + 1 + .. + (k - 1)
所以我们可以预先计算每k的最小值{i = 1 .. l}(a [i] - (k + i - 1))/
动态编程:
规定:
dp [k] [得分] [总和]:我们知道第一个k最小擦除分数,它们的值不超过$ score $,sum是它们的总和。
转换:
Skip score,dp [k] [score] [sum] + = dp [k] [score + 1] [sum];
将$ i $得分值$ score $ dp [k] [得分] [和] + = C [m - k] [i] * dp [k + i] [得+ 1] [总和+ i *得分],其中m个被删除的分数,C [n] [k] =组合。
<强> my code 强>
答案 2 :(得分:0)
胜利的总和应为(N C 2)
减去输入中给出的已知值。设剩余的和(N C 2) - x称为S.输入中-1的数量为Q.
问题现在归结为找到 Q变量的整数解的数量,范围从0到N-1(可能的最大分数),其总和为S
设DP [q] [s]表示q和变量的积分解数,其和为s
然后我们有,
DP[q][s] = Sum (i=0 to N-1) DP[q-1][s-i]
DP [Q] [S]给出解决方案
编辑:
观察:
对于剩下的x人,总胜数应至少为x *(x-1)/ 2(当他们互相比赛时)。因此,在q人的任何时间,s都不能超过(N-q)(N-q-1)/ 2 = M
当s大于M时,DP [q] [s]应该再有一个约束
答案 3 :(得分:0)
我也在尝试解决这个问题,并认为它应该是这样的:
给出了玩家的数量(= N),未知卡的数量(计数“-1”)和已知卡的总和(计算除“-1”之外的所有卡)。可能的游戏总数应为1 + 2 + 3 + ... +(玩家-1):第一个玩家有(玩家-1)对手,第二个玩家(玩家-2)等。
现在您可以递归计算可能的记分卡总和:
使用(玩家,未知牌,已知牌总数)作为关键字以及可能得分卡的总和作为值初始化空的hashmap。
如果定义了所有牌,则答案为0(如果所有牌的总和等于可能的游戏总数)或1(如果所有牌的总和不等于可能的游戏总数)。
如果没有定义所有卡,则运行for循环并将一张未知卡设置为0,1,2 ......(player-1)并尝试从hashmap读取结果。如果它不在hashmap中,则调用方法本身并将结果保存在map中。
递归代码应该是这样的:
def recursion(players: Int, games: Int, unknownCards: Int, knownScore: Int): Int = {
unknownCards match {
case 0 if knownScore != games => 0
case 0 if knownScore == games => 1
case _ =>
map.get(players, unknownCards, knownScore) getOrElse {
var sum = 0
for (i <- 0 until players) sum += main(players, games, unknownCards - 1, knownScore + i)
sum %= 1000000007
map.put((players, unknownCards, knownScore), sum)
sum
}
}
}
答案 4 :(得分:0)
试试这个
import java.util.Scanner;
public class Solution {
final private static int size = 780;
private static long[][] possibleSplits = new long[size][size];
static {
for(int i=0; i < size; ++i)
possibleSplits[i][0] = 1;
for(int j=0; j< size; ++j)
possibleSplits[0][j] = j+1;
for(int i=1; i< size; ++i)
for(int j=1; j < size; ++j)
{
possibleSplits[i][j] = (possibleSplits[i-1][j] + possibleSplits[i][j-1]) % 1000000007;
}
}
public long possibleWays = 0;
public Solution(int n, String scores)
{
long totalScores = 0;
int numOfErasedScores = 0;
for(String str : scores.split(" "))
{
int s = Integer.parseInt(str);
if (s < 0)
++numOfErasedScores;
else
totalScores += s;
}
long totalErasedScores = ncr(n,2) - totalScores;
if(totalErasedScores == 0)
++possibleWays;
else if (totalErasedScores > 0)
partition(n-1, totalErasedScores, numOfErasedScores);
}
private void partition(int possibleMax, long total, int split)
{
if (split == 0)
return;
possibleWays = possibleSplits[(int)total-1][split-1];
if (total > possibleMax)
possibleWays -= split;
}
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
int numberOfTestCases = Integer.parseInt(in.nextLine().trim());
for(int i=0; i< numberOfTestCases; ++i)
{
String str = in.nextLine().trim();
int numberOfPlayers = Integer.parseInt(str);
String playerScores = in.nextLine().trim();
long result = new Solution(numberOfPlayers, playerScores).possibleWays;
System.out.println(result % 1000000007);
}
in.close();
}
public static long ncr(int n, int r)
{
long result = 1;
for(int i= Math.max(n-r, r)+1;i<=n;++i)
result*= i;
result/= fact(Math.min(n-r,r));
return result;
}
public static long fact(int n)
{
long result = 1;
for(int i =2; i<= n; ++i)
result *= i;
return result;
}
}