刷题: bribe the prisoners(2009 Round 1C C)

来源:互联网 发布:神之浩劫等待游戏数据 编辑:程序博客网 时间:2024/05/22 00:19

题目描述

In a kingdom there are prison cells (numbered 1 to P) built to form a straight line segment. Cells number i and i+1 are adjacent, and prisoners in adjacent cells are called "neighbours." A wall with a window separates adjacent cells, and neighbours can communicate through that window.

All prisoners live in peace until a prisoner is released. When that happens, the released prisoner's neighbours find out, and each communicates this to his other neighbour. That prisoner passes it on to his other neighbour, and so on until they reach a prisoner with no other neighbour (because he is in cell 1, or in cell P, or the other adjacent cell is empty). A prisoner who discovers that another prisoner has been released will angrily break everything in his cell, unless he is bribed with a gold coin. So, after releasing a prisoner in cell A, all prisoners housed on either side of cell A - until cell 1, cell P or an empty cell - need to be bribed.

Assume that each prison cell is initially occupied by exactly one prisoner, and that only one prisoner can be released per day. Given the list of Q prisoners to be released in Qdays, find the minimum total number of gold coins needed as bribes if the prisoners may be released in any order.

Note that each bribe only has an effect for one day. If a prisoner who was bribed yesterday hears about another released prisoner today, then he needs to be bribed again.

Input

The first line of input gives the number of cases, N(N表示有N个测试例子)N test cases follow. Each case consists of 2 lines. The first line is formatted as

P Q
where P is the number of prison cells (P是总的监狱数)and Q is the number of prisoners to be released(Q是要释放的罪犯个数).
This will be followed by a line with Q distinct cell numbers (of the prisoners to be released), space separated, sorted in ascending order.

Output(输出格式)

For each test case, output one line in the format

Case #X: C
where X is the case number, starting from 1, and C is the minimum number of gold coins needed as bribes.

Limits

1 ≤ N ≤ 100
Q ≤ P
Each cell number is between 1 and P, inclusive.

Small dataset

1 ≤ P ≤ 100
1 ≤ Q ≤ 5

Large dataset

1 ≤ P ≤ 10000
1 ≤ Q ≤ 100

Sample


Input 
 
Output 
 2
8 1
3
20 3
3 6 14
Case #1: 7
Case #2: 35

Note

In the second sample case, you first release the person in cell 14, then cell 6, then cell 3. The number of gold coins needed is 19 + 12 + 4 = 35. If you instead release the person in cell 6 first, the cost will be 19 + 4 + 13 = 36.


分析: 该问题是求解罪犯释放的顺序, 找出最佳的释放顺序, 从而使得贿赂的代价最小。 解决该问题的关键就是, 释放了某个罪犯之后, 就把连续的牢房分成了两段。 此后两段就相互独立了。 这样, 原来的那个大问题就变成了2个规模小的子问题了, 所以原问题具有最优子结构, 子问题的最优解组合起来就是原始问题的最优解了, 考虑动态规划。 

下面 我们一步步的推导出该问题的解决办法。

如果我们采用暴力求解法, 很容易想到Q个罪犯的释放顺序总共有Q!种可能。 例如, 释放5个罪犯有5! = 120种可能, 释放100个罪犯, 就有100!种可能, 这个数字很大, 接近100 million, 显然此时暴力求解的办法不现实。

现在我们来考虑问题的结构问题。 如果我们选择首先释放罪犯q,  释放完后, 我们的原始问题就划分成了两个完全独立的子问题:即 [1, q -1]和 [q + 1, P]。 这一步的代价就是  这一步所最少需要的金币 + [1, q - 1]最少所需要的金币 +  [q + 1, P] 这三项之和, 其他以此类推, 不难看出该问题具有最优子结构。 这样原始问题为 [1, P], 子问题为[fisrt cells , ast cells]  defines a block。 所以, 不难看出, 该问题具有overlapping sub-problem 和 一个optimal substructure。 这也意味着我们可以使用动态规划解决问题。

Q: P cells, Q different prisoners 需要被释放, 总过有多少个子问题? 是否比总的罪犯释放的顺序数小很多?

A: 首先我们知道, 每一个要释放的罪犯释放后都会产生一个block在其左边, 一个block在其右边, 由于有Q个罪犯, 所有总共会产生 Q + 1个可能的upper cells (one for each released prisoner as well as P itself) and Q + 1 possible lower cells for blocks, 这样总共会产生的子问题的数目就是Q^2)个子问题。 注: 由于每次只能释放1个罪犯, 所以最终得到子问题的数目是这么多, 这个数字显然远远比n!小了。 参考算法导论第三版中文版207页所说, “如果子问题的数目是输入规模的多项式函数(Q), 而我们可以在多项式时间内求解出每个子问题, 那么动态规划的总的运行时间就是多项式阶的”。 

该问题的 动态规划的公式表示更加的直观:

Cost(i, j) = min{Cost(i, k) + cost(k, j) } +  pos[j] - pos[i] - 2 ,  i < k < j; k 是Q中要释放的的罪犯。


参考程序如下:

#include <algorithm>#include <string>using namespace std;int answers[101][101]; // Q + 1int pardoned[102];int minCost(int start, int finish){    if (finish - start < 0)        return 0;    if (answers[start][finish] != -1)        return answers[start][finish];    int minimum = 1<<30; // 放大2的三十次方,即2^(30)    for (int k = start; k <= finish; k++)        minimum = min(minimum, pardoned[finish+1]-pardoned[start-1]-2                 + minCost(start, k - 1) + minCost(k + 1, finish));    answers[start][finish] = minimum;    return minimum;}int main(){    string basePath = "";    string inFile = basePath + "input.txt";    string outFile = basePath + "output.txt";    freopen(inFile.c_str(),"r",stdin);    freopen(outFile.c_str(),"w",stdout);    int testCases;    scanf("%d", &testCases);    for (int caseNo = 1; caseNo <= testCases; caseNo++)    {        int P, Q;        scanf("%d %d", &P, &Q);        for(int i=0; i<Q+1; i++)            for(int j=0; j<Q+1; j++)                answers[i][j] = -1;        pardoned[0] = 0;        pardoned[Q+1] = P+1;        int i;        for (i = 1; i <= Q; i++)            scanf("%d", &pardoned[i]);        printf("Case #%d: %d\n", caseNo, minCost(1, Q));    }    return 0;}
运行之后结果如下:







0 0
原创粉丝点击