Google APAC Test 2017 Round D

来源:互联网 发布:张家界导游软件app 编辑:程序博客网 时间:2024/05/17 06:26

帮同学做的,这几道题质量还是很高的。

Problem A. Vote

A and B are the only two candidates competing in a certain election. We know from polls that exactly N voters support A, and exactly M voters support B. We also know that N is greater than M, so A will win.

Voters will show up at the polling place one at a time, in an order chosen uniformly at random from all possible (N + M)! orders. After each voter casts their vote, the polling place worker will update the results and note which candidate (if any) is winning so far. (If the votes are tied, neither candidate is considered to be winning.)

What is the probability that A stays in the lead the entire time -- that is, that A will always be winning after every vote?

Input

The input starts with one line containing one integer T, which is the number of test cases. Each test case consists of one line with two integers N and M: the numbers of voters supporting A and B, respectively.

Output

For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the probability that A will always be winning after every vote.

y will be considered correct if y is within an absolute or relative error of 10-6 of the correct answer. See the FAQ for an explanation of what that means, and what formats of real numbers we accept.

Limits
1 ≤ T ≤ 100.
Small dataset:0 ≤ M < N ≤ 10.
Large dataset:0 ≤ M < N ≤ 2000.

思路:

读完这道题就觉得像卡特兰数(组合数学教材里引出卡特兰数的例子就是排队找钱问题)。实际上也就是如此:首先如果A一直赢,那么第一局赢的人必然是A。考虑后面n+m-1个序列,就变成了经典的排队找钱问题(考虑任意前k个序列,A赢的次数不能少于B赢的次数)。这个当且仅当的关系非常显然。

接下来考虑n个A和m个B的序列:考虑每一种不合理(一个前缀中B的数量大于A的数量)的结果,找到其“最短不合理前缀”,如ABBABAA是ABB,将此前缀后面的部分翻转(前例会得到:ABBBABB)。那么可知每个不合理的序列等价于一种m-1个A,n+1个B的排列。这个当且仅当的关系也比较容易说明。所以不合理的数量是C(n+m, n) - C(n+m, n+1).

回到原问题:最终的结果就是[ C(n+m-1, n-1) - C(n+m-1, n) ] / C(n+m, n),经过化简得到最终的公式为:(n-m) / (n+m)。非常简单的形式!

#include <cstdio>using namespace std;int T,n,m;int main(){    scanf("%d",&T);    for(int c = 0;c<T;){        scanf("%d %d",&n,&m);        printf("Case #%d: %.9lf\n",++c,(double)(n-m)/(n+m));    }    return 0;}
除此之外,采用动归也能A这道题(包括大数据),思路是dp[i][j]表示长度为i的串中A胜的次数为j的数量。不难分析,dp[i][j] = dp[i-1][j-1]+dp[i-1][j]。对于起点(如dp[5][3])要特判一下。


Problem B. Sitting

The Codejamon game is on fire! Many players have gathered in an auditorium to fight for the World Championship. At the opening ceremony, players will sit in a grid of seats with R rows and C columns.

The competition will be intense, and the players are sensitive about sitting near too many of their future opponents! A player will feel too crowded if another player is seated directly to their left and another player is seated directly to their right. Also, a player will feel too crowded if one player is seated directly in front of them and another player is seated directly behind them. 

What is the maximum number of players that can be seated such that no player feels too crowded? 

Input

The first line of the input gives the number of test cases, T. T test cases follow. Each test case consists of one line with two integers R and C: the number of rows and columns of chairs in the auditorium.

Output

For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the maximum number of players that can be seated, as described in the problem statement.

Limits

1 ≤ T ≤ 100.

Small dataset:1 ≤ R ≤ 5、1 ≤ C ≤ 5.

Large dataset:1 ≤ R ≤ 100、1 ≤ C ≤ 100.

思路:这道题我认为最大的迷惑在于大数据的数据量,看到这个量级直接就往O(R*C^2)这样的dp上去想了,想了一会儿觉得不行。于是看看能不能推公式,推着推着豁然开朗:

引理1:考虑单独一行(假设长度为n)的情况:能够填入的最大数量为:n-n/3。证明简单,略。

引理2:考虑三行(假设长度为n)的情况,能够填入的最大数量为:2*n。证明:首先证明不能再多。因为每一列都是三个位置,根据题意这三个位置不能都填满,所以每一列最多出现两个元素,那么总共最多出现2n个元素;再证明能够达到2n:给出一种填表的方式,第1列的1、2行填入,第2列的1、3行,第3列的2、3行填入,以此规律类推,即可以得到一种符合条件的填入方式。

推论:看看n%3与每行填入数量的关系:首先第一行的数量必为n-n/3(记为k)。那么n%3==0时,三行的数量分别为k,k,k;n%3==1时为k,k,k-1;n%3==2为k,k-1,k-1。

回到原题。行数为n,列数位m,那么如果n>3,先把前n/3个三行填满(根据引理2的填法)。如果剩下一行,那么必然可以填入k,而且根据引理1,这是最大的填法;如果剩下两行,再按照m%3分类,如果m%3!=2,那么根据推论,这两行都可以填入k个元素,显然是最大填法;否则只能一行填k个,另一行填k-1个(这里需要注意一点,这是n>3的情况,如果n==2,那么这两行都可以填入k个),这也是最大填法。

需要注意,因为行数为2可以多填,所以如果n>m先翻转一下为宜(考虑2*5)。

至此显然这道题的复杂度就是O(1),那么R和C限制这么小恐怕就是迷惑用的吧~

#include <cstdio>#include <algorithm>using namespace std;int T,n,m;int main(){    scanf("%d",&T);    for(int c = 0;c<T;){        int res = 0;        scanf("%d %d",&n,&m);        if(n>m)            swap(n, m);        int k = m - m/3;//单独一行能够填入的最大数量        res += n/3 * 2 * m;//填满前面的        if(n % 3 == 1)//剩下一行的情况            res += k;        else if(n % 3 == 2){//剩下两行的情况            if(m%3==2 && n>3)                res += 2*k-1;            else                res += 2*k;        }        printf("Case #%d: %d\n",++c,res);    }    return 0;}

Problem C. Codejamon Cipher

The Codejamon monsters talk in enciphered messages. Here is how it works:

Each kind of monster has its own unique vocabulary: a list of V different words consisting only of lowercase English letters. When a monster speaks, it first forms a sentence of words in its vocabulary; the same word may appear multiple times in a sentence. Then, it turns the sentence into an enciphered string, as follows:

Randomly shuffle each word in the sentence.
Remove all spaces.
Understanding the monsters can bring you huge advantages, so you are building a tool to do that. As the first step, you want to be able to take an enciphered string and determine how many possible original sentences could have generated that enciphered string. For example, if a monster's vocabulary is ["this", "is", "a", "monster", "retsnom"], and it speaks the enciphered string "ishtsiarestmon", there are four possible original sentences:
"is this a monster"
"is this a retsnom"
"this is a monster"
"this is a retsnom"
You have S enciphered strings from the same monster. For each one, can you figure out the number of possible original sentences?

IMPORTANT: Since the output can be a really big number, we only ask you to output the remainder of dividing the result by the prime 109 + 7 (1000000007).

Input:The first line of the input gives the number of test cases, T. T test cases follow. Each test case consists of one line with two integers V and S, the size of the monster's vocabulary and the number of enciphered strings. Then, V lines follow; each contains a single string of lowercase English letters, representing a word in the monster's vocabulary. Finally, S lines follow. Each contains a string consisting only of lowercase English letters, representing an enciphered sentence. It is guaranteed that all enciphered sentences are valid; that is, each one has at least one possible original sentence.

Output:For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is a space separated list of of S integers: the answers (modulo 109 + 7) for each enciphered sentence, in the order given in the input, as described in the problem statement.

Limits:1 ≤ T ≤ 100、1 ≤ S ≤ 5.
Small dataset:

1 ≤ the length of each word in the monster's vocabulary ≤ 5.
1 ≤ the length of the enciphered string ≤ 50.
5 ≤ V ≤ 10.

Large dataset:

1 ≤ the length of each word in the monster's vocabulary ≤ 20.
2000 ≤ the length of the enciphered string ≤ 4000.
200 ≤ V ≤ 400.

思路:这道题实际上是思路最清晰的一道题:就是dp,也不想多说,直接上代码:

#include <cstdio>#include <cstring>#include <string>#include <cstdlib>#include <vector>#include <queue>#include <algorithm>#include <cmath>#include <unordered_map>#include <set>#include <iostream>#include <cstdlib>#define N 100005#define INF 0x7fffffffusing namespace std;int n,m,T;long long dp[4005];int find(unordered_map<string,int> &hh,string x){    sort(x.begin(), x.end());    if(hh.find(x) == hh.end())        return 0;    return hh[x];}int main(){    scanf("%d",&T);    for(int c = 0;c<T;){        string str;        unordered_map<string, int> hh;        cin >> m >> n;        for(int i = 0;i<m;i++){            cin >> str;            sort(str.begin(), str.end());            if(hh.find(str) == hh.end())                hh[str] = 1;            else                hh[str]++;        }        printf("Case #%d:",++c);        for(int l = 0;l<n;l++){            cin >> str;            memset(dp, 0, sizeof(dp));            for(int i = 0;i<str.size();i++){                if(i<20){                    if(int tmp = find(hh,str.substr(0,i+1)))                        dp[i] += tmp;                    dp[i] %= 1000000007;                }                for(int j = i;j>0 && j>i-20;j--)                    if(int tmp = find(hh,str.substr(j,i-j+1))){                        dp[i] += dp[j-1]*tmp;                        dp[i] %= 1000000007;                    }            }            printf(" %lld",dp[str.size()-1]);        }        printf("\n");    }    return 0;}

Problem D. Stretch Rope

Mary likes playing with rubber bands. It's her birthday today, and you have gone to the rubber band shop to buy her a gift.

There are N rubber bands available in the shop. The i-th of these bands can be stretched to have any length in the range [Ai, Bi], inclusive. Two rubber bands of range [a, b] and [c, d] can be connected to form one rubber band that can have any length in the range [a+c, b+d]. These new rubber bands can themselves be connected to other rubber bands, and so on.

You want to give Mary a rubber band that can be stretched to a length of exactly L. This can be either a single rubber band or a combination of rubber bands. You have M dollars available. What is the smallest amount you can spend? If it is impossible to accomplish your goal, output IMPOSSIBLE instead.

Input:

The first line of the input gives the number of test cases, T. T test cases follow. Each test case starts with 3 integers N, M, L, the number of rubber bands available in the shop, the number of dollars you have and the desired rubber band length. Then N lines follow. Each line represents one rubber band and consists of 3 integers, Ai, Bi, and Pi. [Ai, Bi] is the inclusive range of lengths that the i-th rubber band can stretch to, and Pi is the price of the i-th rubber band in dollars.

Output:

For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is IMPOSSIBLE if you cannot buy rubber bands to satisfy the goal described above, or otherwise an integer: the minimum price you can pay.

Limits:

1 ≤ T ≤ 100.
1 ≤ Pi ≤ M.
1 ≤ L ≤ 10000.
1 ≤ Ai ≤ Bi ≤ 10000.

Small dataset:1 ≤ N ≤ 10、1 ≤ M ≤ 100.

Large dataset:1 ≤ N ≤ 1000、1 ≤ M ≤ 1000000000.

思路:此题也有一个类似背包的显然的dp思路。dp[j]表示拼成长度为j所需要的最小的钱数。对于每个绳子(长度区间[a,b],价值为w),从右向左扫一遍dp进行更新:dp[x] = min(dp[x], min(dp[x-b]...dp[x-a]) + w)。这样看下来复杂度是O(N*L*L)有点大。

一个显然的改进是求最小值的时候用RMQ(对于每一个绳子,要更新一下RMQ数组),这样的复杂度是O(N*L*logL),大数据大概8s跑完。

#include <cstdio>#include <cstring>#include <string>#include <cstdlib>#include <vector>#include <queue>#include <algorithm>#include <cmath>#include <map>#include <set>#include <iostream>#define N 100005#define INF 0x3fffffffusing namespace std;int n,m,l,T;struct node{    int a,b,w;}s[1005];int dp[2][10005];int st[10005][20];void makest(int *t){    int k = log2(double(l+1));    for(int i = 1;i<=l;i++)        st[i][0] = t[i];    for(int j = 1;j<=k;j++)        for(int i = 1;i+(1<<j)-1<=l;i++)            st[i][j] = min(st[i][j-1], st[i+(1<<(j-1))][j-1]);}int query(int a,int b){    int k = log2(double(b-a+1));    return min(st[a][k], st[b-(1<<k)+1][k]);}int main(){    scanf("%d",&T);    for(int c = 0;c<T;){        scanf("%d %d %d",&n,&m,&l);        for(int i = 0;i<n;i++)            scanf("%d %d %d",&s[i].a,&s[i].b,&s[i].w);        for(int i = 1;i<=l;i++)            dp[0][i] = dp[1][i] = INF;        dp[0][0] = dp[1][0] = 0;        int a = 0,b = 1;        for(int i = 0;i<n;i++){            makest(dp[a]);            for(int j = l;j>=1;j--){                dp[b][j] = min(dp[b][j], dp[a][j]);                if(j>=s[i].a && j<=s[i].b)                    dp[b][j] = min(dp[b][j], s[i].w);                int begin = max(1, j-s[i].b);                int end = j-s[i].a;                if(begin <= end){                    int tmp =query(begin, end)+s[i].w;                    if(tmp <= m)                    dp[b][j] = min(dp[b][j], query(begin, end)+s[i].w);                }            }            b = 1-b;            a = 1-a;        }        if(dp[a][l] == INF)            printf("Case #%d: IMPOSSIBLE\n",++c);        else            printf("Case #%d: %d\n",++c,dp[a][l]);    }    return 0;}
0 0
原创粉丝点击