Round C APAC Test 2016

题目链接:https://code.google.com/codejam/contest/4284487

Problem A. gRanks

There are many great athletes in the world, and it's very hard to say who is the best in the world at a particular sport, especially when different athletes have won different competitions. Here's one possible system for ranking athletes:

1. Determine the number P of finishing places in any competition that will be worth points for athletes, and how many points Si each of those finishing places is worth. For example, for P = 3, one possible assignment would be 1000 points for 1st place, 500 for 2nd place, and 300 for 3rd place, and 0 for anything below that. (We assume there are no ties within competitions.)

2. Since not all competitions are equally important, assign a weight Wi to each one. The score gained by an athlete for a competition will be the points from step 1, modified by the weight for that competition. For example, we may decide that Olympics has a weight of 5, and, continuing with our example from above, the winner of the Olympics would receive 5 * 1000 = 5000 points.

3. Since we don't want to reward athletes simply for participating in many competitions, we count only the M highest scores received by an athlete across all competitions. For example, if M = 2 and an athlete earns scores of 1000*5, 500*1, and 300*3 in three different competitions, only the 5000 and 900 would be counted.

You are given the points per finishing place, the weights of the competitions, and the results of the competitions. Can you rank all of the athletes who appeared in the competitions? If multiple athletes have the same score, they will share the same rank and listed in alphabetical order of their names.

Input

The first line of the input gives the number of test cases, T. T test cases follow; each test case consists of the following:

1. One line with an integer P, the number of top places for which points are awarded.
2. One line consists with P integers representing the scores Si for the top places, starting with first place and continuing down the places.
3. One line with an integer N, the number of competitions. 4. N lines, each of which represents a competition. Each line begins with Wi, the weight of this competition, and continues with the P names of the athletes who finished in the top P places. They are listed in descending order starting from first place.
5. One line with an integer M, the maximum number of competitions counted toward an athlete's score.
Output

For each test case, output one line containing "Case #x:", where x is the test case number (starting from 1). Then output one line for each athlete, from highest rank to lowest rank, with the format r: name, where r is the rank of the athlete and name is the name of the athlete. You need to rank all of the athletes that appeared in the input.
Limits

题意:给了一个评分系统,求运动员的排名。P表示每场比赛前多少名次有积分,然后给出具体积分。接下来n行表示n个比赛,每行第一个数是这个比赛的权重,之后从第一名开始给出运动员的名字。最后给一个数m表示取前m高的积分(防止过多参加比赛赚取积分)。

思路:因为数据范围较小,所以其实给每个运动员一个数据记录积分,之后排序取前m即可。这里用了堆其实是不必要的。

#include <cstdio>
#include <string>
#include <queue>
#include <unordered_map>
#include <algorithm>
#include <iostream>
using namespace std;
#define N 1005
int T,n,m,w,k;
int s[N],len = 0;
unordered_map<string, int> hh;//运动员名字到对应的堆的映射
string name;
struct node{
    string s;
    int v;
}p[N*10];
int cmp(node a,node b){
    if(a.v == b.v)
        return a.s < b.s;
    return a.v > b.v;
}
int main(){
    cin >> T;
    for(int z = 1;z<=T;z++){
        cin >> m;
        priority_queue<int> q[N*10];
        len = 0;
        hh.clear();
        for(int i = 0;i<m;i++)
            cin >> s[i];
        cin >> n;
        for(int i = 0;i<n;i++){
            cin >> w;
            for(int j = 0;j<m;j++){
                cin >> name;
                if(hh.find(name) == hh.end())
                    hh[name] = len++;
                q[hh[name]].push(w*s[j]);
            }
        }
        cin >> k;
        unordered_map<string, int> ::iterator it;
        len = 0;
        for(it = hh.begin();it!=hh.end();++it){
            p[len].s = it->first;
            p[len].v = 0;
            int tmp = it->second;
            for(int i = 0;!q[tmp].empty() && i<k ;i++){
                p[len].v += q[tmp].top();
                q[tmp].pop();
            }
            len++;
        }
        sort(p, p+len, cmp);
        cout << "Case #"<<z<<":"<<endl;
        for(int i = 0;i<len;){
            cout<<i+1<<": "<<p[i].s<<endl;
            int j = i+1;
            while(j<len && p[j].v == p[j-1].v){
                cout<<i+1<<": "<<p[j].s<<endl;
                j++;
            }
            i = j;
        }
    }
    return 0;
}


Problem B. gFiles

Alien tech company G has a very old file transfer tool that is still in use today. While the tool is running, it reassures users by giving status updates on both the percentage of files transferred so far and the number of files transferred so far. The status updates during the process might look like this:

20% |==>-------| 1 files transferred
100% |==========| 5 files transferred
But the percentage isn't precise; it is simply truncated before the decimal point (i.e. floored to the next lowest or equal 1%). That is, both 1.2% and 1.7% would be displayed as 1%.
Some users may want to know the exact total number of files, so you want to modify the tool so that the status updates look like this:
20% |==>-------| 1 out of 5 files transferred
100% |==========| 5 out of 5 files transferred
But you've realized that it may or may not be possible to know the number of files. Given the status updates that the tool displays, either figure out how many files there are, or determine that it can't be done (i.e., there are multiple possible values for the number of files). The status updates are not guaranteed to occur at regular intervals and are not guaranteed to occur whenever a file is transferred.

Input

The first line contains T, the number of test cases. T test cases follow. Each test case consists of one line with an integer N, the number of status updates output by the tool, followed by N lines with the format Pi Ki, where Pi and Ki are integers representing the percentage and number of files transferred at some point in the process. The updates are given listed in chronological order -- that is, both the Pi values and the Ki values are nondecreasing throughout a test case.

Output

For each case, output a line starts with "Case #x: y", where x is the number of the case (starting from 1), and y is either the total number of files, or -1 if that number is ambiguous.

题意:给定一个百分比和已经传输的文件数量,问能否准确猜出一共有多少文件,如果结果有歧义输出-1.

思路:二分,如果没有合理方案,必然输出-1;如果有,那么看看该方案减一和加一是否合理,如果有合理的,那么输出-1,否则输出方案即可。因为不可能出现两个合理方案中间夹着不合理的情况。特别注意如果方案小于输入中的最大文件数必然是错误的(体现在二分时候的下界,和test函数一开始的return)

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 105
long long s[N][2];
int T,n;
int test(long long x){
    if(x<s[n-1][1])
        return -1;
    for(int i = 0;i<n;i++){
        long long tmp = s[i][1]*100/x;
        if(tmp < s[i][0])
            return -1;
        if(tmp > s[i][0])
            return 1;
    }
    return 0;
}
long long solve(long long low,long long high){
    long long mid;
    while(low <= high){
        mid = (low+high)>>1;
        int j = test(mid);
        if(j<0)
            high = mid-1;
        else if(j>0)
            low = mid+1;
        else
            return mid;
    }
    return -1;
}
int main(){
    scanf("%d",&T);
    for(int z = 1;z<=T;z++){
        printf("Case #%d: ",z);
        long long flag = 0;
        scanf("%d",&n);
        for(int i = 0;i<n;i++)
            scanf("%lld %lld",&s[i][0],&s[i][1]);
        flag = solve(s[n-1][1], 1000000000000000);
        if(flag != -1){
            if(test(flag-1) && test(flag+1))
                printf("%lld\n",flag);
            else
                printf("-1\n");
        }
        else
            printf("-1\n");
        
    }
    return 0;

Problem C. gGames

The country of elves is planning to hold an elimination tournament, and there are 2N elves who would like to take part. At the start of the tournament, they will be given unique ID numbers from 1 to 2N, and the Elf President will line them up in some order.


The tournament is a series of matches between two elves, and every match has one winner and one loser (there are no ties). In the first round, the first elf in the line competes against the second elf in the line, the third elf competes against the fourth elf, and so on. After the first round, the 2N-1 elves who lost leave the line, and the 2N-1 elves who won remain where they are. Then, the remaining elves play the second round in the same way: the first remaining elf in the line competes against the second remaining elf in the line, the third remaining elf competes against the fourth remaining elf, and so on. After N rounds, there will be only one elf remaining, and that elf is the winner.

M of the elves are sensitive, which means that they will be very sad if they have to compete in matches against their friends during the games. Specifically, the ith elf will be sad if they have to compete with their friends in the first Ki rounds. (Note that friendship is not necessarily mutual: if one elf considers another elf to be a friend, the other elf does not necessarily consider that elf to be a friend.)

The Elf President wants to know: is there a way to specify the initial positions of all 2N elves to guarantee that no elf will be sad, no matter what happens in the tournament?

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 N and M, then M sets of two lines each, in which the first line has integers Ei, Ki, and Bi for one elf, and the second has Bi integer ID numbers of that elf's friends.

Output

For each test case, output one line containing "Case #x: ", where x is the case number (starting from 1), followed by YES or NO.

Limits

1 ≤ T ≤ 200.
0 ≤ M ≤ 2N.
1 ≤ Ei ≤ 2N.
1 ≤ Ki ≤ N.
M ≤ sum(Bi) ≤ min(2 * M, 2N).

Small dataset

1 ≤ N ≤ 3.

Large dataset

N = 4.

题意:现在想把2^n个人(n<=4)排成一排进行锦标赛。给出若干个限制,比如a和b不能在第1轮相遇等等。问符合限制的排列是否存在。

思路:一看数据觉得是搜索加剪枝,但是手写了一个发现剪枝的效果不明显。后来知道是用状态压缩dp来做。首先用0~2^16之间的数字表示一个分配,下标就表示人,对应数字为1的下标表示放在一起,dp值为1表示可行,为0表示不可行(注意1的数量必然是2的幂次)。比如dp[5]表示把第1个人和第3个人放在一起(即第一轮相遇),而dp[27]表示将第1、2、4、5个人放在一个区域。以dp[27]为例,先检查两两之间在第二轮相遇是否有限制,如果有某两个不允许相遇,那么直接返回dp[27]=0;否则枚举平分递归判断下去。

#include <cstdio>
#include <cstring>
#include <vector>
#include <cstdlib>
using namespace std;
#define N (1<<n)
int T;
int n,m;
int g[20][20];
int dp[1<<16];
vector<int> flag[4];
int dfs(int x, int d){
    if(dp[x] != -1)
        return dp[x];
    if(d==0)
        return dp[x] = 1;
    vector<int> hh;
    for (int i = 0; i<N; ++i)
        if ((1<<i) & x)
            hh.push_back(i);
    for (int i = 0; i < hh.size(); ++i)
        for (int j = i+1; j < hh.size(); ++j)
            if (g[hh[i]][hh[j]] >= d)
                return dp[x] = 0;
    
    auto len = flag[d].size();
    for(int i = 0;i<len;i++){
        int a = 0;
        for(int j = 0;j<(1<<d);++j)
            if((1<<j)&flag[d][i])
                a |= 1<<hh[j];
        int b = x-a;
        if(a<b && dfs(a, d-1) && dfs(b, d-1))
            return dp[x] = 1;
    }
    return dp[x] = 0;
    
}
int onenum(int x){
    int res = 0;
    while (x) {
        res ++;
        x &= (x-1);
    }
    return res;
}
int main(){
    for(int i = 1;i<=4;i++)//flag[i]表示从2^i中选择2^(i-1)个数的方案
        for(int j = 1;j<(1<<(1<<i));j++){
            int tmp = onenum(j);
            if(tmp == 1<<(i-1))
                flag[i].push_back(j);
        }
    scanf("%d",&T);
    for(int z = 1;z<=T;z++){
        int a,b,k,num;
        scanf("%d %d",&n,&m);
        memset(g, -1, sizeof(g));
        memset(dp, -1, sizeof(dp));
        for(int i = 0;i<m;i++){
            scanf("%d %d %d",&a,&k,&num);
            while(num--){
                scanf("%d",&b);
                g[a-1][b-1] = g[b-1][a-1] = max(g[a-1][b-1], k);
            }
        }
        if(dfs((1<<N)-1, n))
            printf("Case #%d: YES\n",z);
        else
            printf("Case #%d: NO\n",z);
    }
    return 0;
}


Problem D. gMatrix

You have a square N by N matrix M of nonnegative integers. We would like to make a list of the maximum values in every sub-matrix of size K by K within M, and then find the sum of those values together. (Note that the same entry of M might be the maximum value in more than one sub-matrix, in which case it will show up multiple times in the list.) Can you find that sum?

To simplify the input of the matrix, you are given two arrays A and B of length N, and two integers C and X. Then the entry Mij (for the ith row and jth column of the matrix) equals (Ai*i+Bj*j + C) mod X, where i and j are in the range [1, N].

Input

The first line of the input gives the number of test cases, T. T test cases follow. Each test case begins with one line with four integers, N, K, C and X. Then there are two lines with N integers each, representing the arrays A and B.

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 sum of the maximum values in all sub-matrices of size K by K.

题意:给定一个n*n矩阵,求所有k*k子矩阵中最大值之和。

思路:有点类似最大子矩阵的做法。维护每列k个元素的最大值(即s[0][0]...s[k][0],s[0][1]..s[k][1]....s[0][n]...s[k][n]),然后再求这n个值每k个元素的最大值。这个问题通过deque有一个O(n)的算法。所以总体的复杂度是O(n*n)

#include <cstdio>
#include <string>
#include <queue>
#include <unordered_map>
#include <algorithm>
#include <iostream>
using namespace std;
#define N 3005
int s[N][N],t[N];
long long a[N],b[N];
int T,n,c,x,k;
struct node{
    node(int ii,int xx):i(ii),x(xx){};
    int i,x;
};
void mypush(deque<node> &q,node tmp){
    while(!q.empty() && tmp.x>=q.back().x)
        q.pop_back();
    q.push_back(tmp);
}
long long solve(){
    deque<node> ans;
    long long res = 0;
    for(int i = 0;i<k-1;i++)
        mypush(ans, node(i,t[i]));
    for(int i = k-1;i<n;i++){
        mypush(ans, node(i,t[i]));
        if(ans.front().i == i-k)
            ans.pop_front();
        res += ans.front().x;
    }
    return res;
}
int main(){
    scanf("%d",&T);
    for(int z = 1;z<=T;z++){
        long long res = 0;
        deque<node> q[N];
        scanf("%d %d %d %d",&n,&k,&c,&x);
        for(int i = 0;i<n;i++)
            scanf("%lld",&a[i]);
        for(int i = 0;i<n;i++)
            scanf("%lld",&b[i]);
        for(int i = 0;i<n;i++)
            for(int j = 0;j<n;j++)
                s[i][j] = (a[i]*(i+1) + b[j]*(j+1) + c)%x;
        for(int j = 0;j<n;j++)
            for(int i = 0;i<k-1;i++)
                mypush(q[j],node(i,s[i][j]));
        for(int i = k-1;i<n;i++){
            for(int j = 0;j<n;j++){
                mypush(q[j],node(i,s[i][j]));
                if(q[j].front().i == i-k)
                    q[j].pop_front();
                t[j] = q[j].front().x;
            }
            res += solve();
        }
        printf("Case #%d: %lld\n",z,res);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值