【在线笔试题解题报告系列】Google APAC 2017 University Test Round A

基本什么都没复习(最多前一天晚上看了一下今年的Practice Round),然后就冲上来做了。

scoreboard中搜索hdu.toraoh,可以看我的当时实际提交情况。


题意就不翻译了,这种英文阅读应该是能搞掂的,不然真没法在IT外企工作了。


Problem A. Country Leader

设计一个结构体,能读入,读完顺手计算一下有多少不同的英文字母(注意英文字母不包括空格),然后顺手让这个结构体还能比较。最后不管你是O(nlogn)排序取最小,还是边读,边只保留最小,都行。


#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
#include <set>
using namespace std;
typedef long long ll;

int T,Case=1;

struct Name{
    char s[105];
    int diff;
    void get(){
        gets(s);
        set<char> ss;
        for(int i=0;s[i];++i){
            if(s[i]!=' ')ss.insert(s[i]);
        }
        diff=ss.size();
    }
    bool operator<(const Name&b)const{
        if(diff!=b.diff)return diff>b.diff;
        return strcmp(s,b.s)<0;
    }
}name[105];

int main(){
    freopen("A-large.in","r",stdin);
    freopen("A-large.txt","w",stdout);
    int n;
    for(scanf("%d",&T);Case<=T;Case++){
        scanf("%d%*c",&n);
        for(int i=0;i<n;i++)name[i].get();
        sort(name,name+n);
        
        printf("Case #%d: ",Case);
        puts(name[0].s);
    }
    return 0;
}

Problem B. Rain

造成了比较大的精神伤害(和麻烦)的一题……

我的思路是,反其道而行之,你从高处流下来流到低洼处不走了,我反过来,找低洼处,从下面往上灌水,直到找不到能灌水的低洼处为止。

具体实现上,首先是预处理。

按高度块合并,然后把外圈靠海的那些块标记为和大海这个特殊块相连。

之后还做了一堆,比如和大海相连的块出发bfs,找更高的块,也标记为边缘块,等(是否有用?我不确定)。

然后在中间,不靠海的块按高度放入优先队列。

之后,每次从优先队列里抓一块出来,搜索其边缘块,看看这个面积区域积水能积多深。

如果不能积水(边缘块比这块还矮),这块就不考虑了。

否则,直接水灌进去,和边上同高度的合并起来。

如果还是不和边缘块在一起,放回优先队列,否则,也不应该灌水了,不放回优先队列。

这样一直处理,直到优先队列为空。

最后,统计灌进去多少水,输出。


——你看我洋洋洒洒扯了这么麻烦的一个办法,然后我代码也有200行这么长……

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;

const int di[4][2]={{-1,0},{1,0},{0,1},{0,-1}};

int T,Case=1;
int r,c;
int orimap[55][55];
int tarmap[55][55];

int fat[3000];

int ptoid(int x,int y){
    return x*c+y;
}

int find(int x){
    return fat[x]==x?x:fat[x]=find(fat[x]);
}

bool join(int a,int b){
    int fa=find(a),fb=find(b);
    if(fa==fb)return false;
    fat[fa]=fb;
    return true;
}

struct Point{
    int x,y;
    Point(){}
    Point(int a,int b):x(a),y(b){}
    int ptoid(){
        return x*c+y;
    }
    Point go(int d){
        return Point(x+di[d][0],y+di[d][1]);
    }
    bool inrange(){
        return x>=0 &&x<r&&y>=0&&y<c;
    }
    bool operator<(const Point&b)const{
        if(x!=b.x)return x<b.x;
        return y<b.y;
    }
};

bool vis[55][55];

int getMinHeight(Point p){
    memset(vis,false,sizeof(vis));
    int res=INT_MAX;
    queue<Point> q;
    q.push(p);
    vis[p.x][p.y]=true;
    while(!q.empty()){
        Point x=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            Point p2=x.go(i);
            if(vis[p2.x][p2.y])continue;
            if(find(p2.ptoid())!=find(p.ptoid())){
                res=min(res,tarmap[p2.x][p2.y]);
            }else{
                vis[p2.x][p2.y]=1;
                q.push(p2);
            }
        }
    }
    return res;
}

void setMinHeight(Point p,int h){
    memset(vis,false,sizeof(vis));
    queue<Point> q;
    q.push(p);
    vis[p.x][p.y]=true;
    tarmap[p.x][p.y]=h;
    vector<Point> joinList;
    while(!q.empty()){
        Point x=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            Point p2=x.go(i);
            if(vis[p2.x][p2.y])continue;
            if(find(p2.ptoid())!=find(p.ptoid())){
                if(tarmap[p2.x][p2.y]==h){
                    joinList.push_back(p2);
                }
            }else{
                tarmap[p2.x][p2.y]=h;
                vis[p2.x][p2.y]=1;
                q.push(p2);
            }
        }
    }
    for(int i=0;i<joinList.size();i++){
        join(joinList[i].ptoid(),p.ptoid());
    }
}

void joinHeigher(Point p){
    memset(vis,false,sizeof(vis));
    queue<Point> q;
    q.push(p);
    vis[p.x][p.y]=true;
    while(!q.empty()){
        Point x=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            Point p2=x.go(i);
            if(!p2.inrange())continue;
            if(vis[p2.x][p2.y])continue;
            if(orimap[p2.x][p2.y]>orimap[x.x][x.y]){
                join(p2.ptoid(),x.ptoid());
                q.push(p2);
            }
        }
    }
}

int main(){
    freopen("B-large.in","r",stdin);
    freopen("B-large.txt","w",stdout);
    
    for(scanf("%d",&T);Case<=T;Case++){
        scanf("%d%d",&r,&c);
        for(int i=0;i<=r*c+1;++i)fat[i]=i;
        for(int i=0;i<r;i++){
            for(int j=0;j<c;j++){
                scanf("%d",&orimap[i][j]);
                tarmap[i][j]=orimap[i][j];
            }
        }
        
        int boundId=ptoid(r,0);
        
        for(int i=0;i<r;i++){
            join(boundId,ptoid(i,0));
            joinHeigher(Point(i,0));
            join(boundId,ptoid(i,c-1));
            joinHeigher(Point(i,c-1));
        }
        for(int i=0;i<c;i++){
            join(boundId,ptoid(0,i));
            joinHeigher(Point(0,i));
            join(boundId,ptoid(r-1,i));
            joinHeigher(Point(r-1,i));
        }
        
        for(int i=1;i<r-1;i++){
            for(int j=1;j<c-1;j++){
                for(int k=0;k<4;k++){
                    int px=i+di[k][0];
                    int py=j+di[k][1];
                    if(orimap[px][py]==orimap[i][j]){
                        join(ptoid(i,j),ptoid(px,py));
                    }
                }
            }
        }
        
        priority_queue< pair<int,Point>,vector<pair<int,Point>>,greater<pair<int,Point>>> q;
        
        for(int i=1;i<r-1;i++){
            for(int j=1;j<c-1;j++){
                if(find(ptoid(i,j))==ptoid(i,j) && ptoid(i,j)!=find(boundId)){
                    q.push(make_pair(orimap[i][j],Point(i,j)));
                }
            }
        }
        
        while(!q.empty()){
            pair<int,Point> x=q.top();q.pop();
            if(find(x.second.ptoid())!=x.second.ptoid())continue;
            int h=getMinHeight(x.second);
            if(h<tarmap[x.second.x][x.second.y])continue;
            setMinHeight(x.second,h);
            if(find(x.second.ptoid())!=find(boundId)){
                q.push(make_pair(h,x.second));
            }
        }
        
        int ans=0;
        for(int i=1;i<r-1;i++){
            for(int j=1;j<c-1;j++){
                ans+=tarmap[i][j]-orimap[i][j];
            }
        }
        
        printf("Case #%d: %d\n",Case,ans);
    }
    return 0;
}


Problem C. Jane's Flower Shop


这题的重点是,不要被数学公式吓跑,不要考虑求导,把题读完。

题目描述部分的最后一句话是:

It is guaranteed that -1 < r < 1, and there is exactly one solution in each test case.

r=-1时,显然有f(-1)>=0,要有且仅有一解,显然f(1)<=0不说,在解和-1之间必然同号,解和1之间也是同号——那就是保证有[-1,x)是正的,(x,1]是负的。

那很直白的思路了:二分查找,是正的,缩小解区间左边,否则缩小解区间右边。


注意到这题要求精度达到1e-9,这不是什么低精度要求……

所以,二分的绝对误差要求高一点,中间的计算尽量用高精度的数据类型(听说C/C++要long double才能过,我在似乎无所谓的地方用了一下,也过了╮(╯_╰)╭)


——Google你真的没把B和C放反吗?

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
using namespace std;
typedef long long ll;

int T,Case=1;
int n;
int val[105];

long double cal(double x){
    long double v=-val[0]*pow(1+x,n);
    for(int i=1;i<=n;i++){
        v+=val[i]*pow(1+x,n-i);
    }
    return v;
}

int main(){
    freopen("C-large.in","r",stdin);
    freopen("C-large.txt","w",stdout);
    
    for(scanf("%d",&T);Case<=T;Case++){
        scanf("%d",&n);
        for(int i=0;i<=n;i++)scanf("%d",&val[i]);
        double l=-1.0,r=1.0,mid;
        while(r-l>1e-12){
            mid=(l+r)/2;
            if(cal(mid)>0.0)l=mid;
            else r=mid;
        }
        
        printf("Case #%d: %.12f\n",Case,l);
    }
    return 0;
}

Problem D. Clash Royale

事实上我被以前大家的过题情况骗了,然后想着小数据分拿到就好……

完全相反的是,D的大数据的分数是很好拿到的,你不需要成为动态规划玩得很溜的大神。D的大数据其实是,套路。


D的小数据的直白过法就是,分组背包。

然后我莫名其妙地开始脑抽,打死都要swap、clear,坑了好半天,最后剩20分钟的时候还是调通了……

代码很短,这个写法也不是这题的重点,贴一下,感兴趣自己上网查分组背包这东西。

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;

int T,Case=1;
int m,n;
int Ki[10],Li[10];
int A[10][15],C[10][15];

vector<int> last(1005),cur(1005);

int main(){
    freopen("D-small-attempt1.in","r",stdin);
    freopen("D-small1.txt","w",stdout);
    
    for(scanf("%d",&T);Case<=T;Case++){
        scanf("%d%d",&m,&n);
        
        for(int i=0;i<n;i++){
            scanf("%d%d",&Ki[i],&Li[i]);
            for(int j=1;j<=Ki[i];j++)scanf("%d",&A[i][j]);
            for(int j=2;j<=Ki[i];j++)scanf("%d",&C[i][j]);
        }
        fill(last.begin(),last.end(),0);
        for(int i=0;i<n;i++){
            last[0]+=A[i][Li[i]];
        }
        for(int i=1;i<=m;i++)last[i]=last[0];
        
        for(int i=0;i<n;i++){
            int premon=0;
            cur=last;
            for(int j=Li[i]+1;j<=Ki[i];++j){
                premon+=C[i][j];
                for(int l=m;l>=premon;--l){
                    cur[l]=max(max(cur[l],last[l]),last[l-premon]+A[i][j]-A[i][Li[i]]);
                }
            }
            last=cur;
        }
        
        printf("Case #%d: %d\n",Case,last[m]);
    }
    return 0;
}

重点是,D的大数据。

注意到,N<=12,必须选8个

C(12,8)=495,很小啊。

然后考虑枚举选择的组合之后,怎么办?


还记得Practice Round的Problem B. Robot Rock Band吗?怎么做大数据呢?

4组,分成2部分,AB和CD,AB暴力计算完,放进HashMap,待用,之后枚举CD,在AB的HashMap中找,累加结果,复杂度变成了O(n^2+n^2*(1~logn))=O(n^2)

这种思想呢,你可以称作,meet in the middle。

概括起来就是,左边一半管自己的,怎么暴力怎么来,右边一半也是如此。把左右两边的结果结合起来的时候,使用一些高效的办法(单次操作一般O(1)、O(logn)的办法)。


这种思路能借鉴过来吗?Yes!


------以下是参考思路------











选出8个后,分成2部分处理,前4种卡牌和后4种卡牌。

对前4种卡牌,先暴力枚举,因为只有10档升级,所以最多有10^4种情况。


为了效率,以及之后与后半部分的结合的便捷性,我们考虑只留下有效数据。

所谓有效数据,就是对这种组合方案,不存在比他便宜或一样便宜的东西,战斗力更强。

很简单。

1、对之前算出来不超预算的情况,按价格从小到大,同价格按战力从大到小排序。

2、排序后,从前到后一个个看,记录前面最高战力,只保留能更新最高战力的数据(想一想为什么)


那对后半部分的处理就比较显然了。

同样的暴力枚举,是否只留下有效数据看你个人喜好(影响不大,这边再这么操作,那也只是常数优化)

对每种组合方案,在前4种卡牌中,找价格不超过剩余钱数的,战力最大的方案。根据这个结果,更新最优方案。

之前排序处理好有效数据的话,用二分查找,一次查找时间复杂度只要O(logn)。


最后考虑一下,我们的程序多久能跑完:

每组数据,要枚举C(12,8)=495种情况

每种情况下,计算量为10^4(前半部分的生成)+10^4*log(10^4)(排序)+10^4(筛选有效数据)+10^4*log(10^4)(后半部分的生成与二分查找)~= 280000

所以,一组数据大致计算量为495*280000=1.386e8

考虑到现在的电脑一般能做到1秒计算1.8e8(1秒计算1e8的,那是奔腾4),也就意味着,一组数据能在1秒之内计算出来,100组数据也就100秒,

你可以先小数据上确认正确性,然后下载大数据,运行,上传——别忘了大数据提交时间窗口是8分钟,跑100秒也不紧张。

实际上对Practice模式提供的大数据,我只跑了20秒,当然,我的代码里还有很多常数优化空间。


#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;

int T,Case=1;
ll m,n;
int Ki[15],Li[15];
ll A[15][15],C[15][15];

ll bestans;
bool sel[15];

int itemList[8];

struct Item{
    ll money,power;
    Item(){}
    Item(ll a,ll b):money(a),power(b){}
    bool operator<(const Item&b)const{
        if(money!=b.money)return money<b.money;
        return power>b.power;
    }
};

vector<Item> tmp,t1;

void check(){
    tmp.clear();t1.clear();
    for(int i=Li[itemList[0]];i<=Ki[itemList[0]];++i)
        for(int j=Li[itemList[1]];j<=Ki[itemList[1]];++j)
            for(int k=Li[itemList[2]];k<=Ki[itemList[2]];++k)
                for(int l=Li[itemList[3]];l<=Ki[itemList[3]];++l){
                    Item nnew(C[itemList[0]][i]+C[itemList[1]][j]+C[itemList[2]][k]+C[itemList[3]][l],
                                A[itemList[0]][i]+A[itemList[1]][j]+A[itemList[2]][k]+A[itemList[3]][l]);
                    if(nnew.money<=m)tmp.push_back(nnew);
                }
    sort(tmp.begin(),tmp.end());
    ll maxpow=0;
    for(int i=0;i<tmp.size();i++){
        if(tmp[i].power<=maxpow)continue;
        maxpow=tmp[i].power;
        t1.push_back(tmp[i]);
    }

    for(int i=Li[itemList[4]];i<=Ki[itemList[4]];++i)
        for(int j=Li[itemList[5]];j<=Ki[itemList[5]];++j)
            for(int k=Li[itemList[6]];k<=Ki[itemList[6]];++k)
                for(int l=Li[itemList[7]];l<=Ki[itemList[7]];++l){
                    Item nnew(C[itemList[4]][i]+C[itemList[5]][j]+C[itemList[6]][k]+C[itemList[7]][l],
                                A[itemList[4]][i]+A[itemList[5]][j]+A[itemList[6]][k]+A[itemList[7]][l]);
                    if(nnew.money<=m){
                        Item nn2(m-nnew.money,0);
                        auto it=upper_bound(t1.begin(),t1.end(),nn2);
                        //while(it!=t1.end()&&it->money+nnew.money<m)++it;
                        while(it==t1.end()||it->money+nnew.money>m)--it;
                        auto x=*it;
                        bestans=max(bestans,x.power+nnew.power);
                    }
                }
    
}

void dfs(int depth,int last){
    if(depth==8){
        check();
        return ;
    }
    if(8-depth>n-last)return;
    for(int i=last+1;i<n;i++){
        itemList[depth]=i;
        dfs(depth+1,i);
    }
}

int main(){
    freopen("D-large-practice.in","r",stdin);
    freopen("D-large.txt","w",stdout);
    
    for(scanf("%d",&T);Case<=T;Case++){
        bestans=0;
        
        scanf("%I64d%I64d",&m,&n);
        
        for(int i=0;i<n;i++){
            scanf("%d%d",&Ki[i],&Li[i]);
            for(int j=1;j<=Ki[i];j++)scanf("%I64d",&A[i][j]);
            for(int j=2;j<=Ki[i];j++)scanf("%I64d",&C[i][j]);
            C[i][Li[i]]=0;
            for(int j=Li[i]+1;j<=Ki[i];j++)C[i][j]+=C[i][j-1];
        }
        dfs(0,-1);
        printf("Case #%d: %I64d\n",Case,bestans);
    }
    return 0;
}


啰嗦了这么多呢……辛苦阅读到这里的各位了。

你可能感兴趣的:(test,谷歌,APAC,google校招)