笔试地址
这周做了下google的在线笔试第二轮,总结一下。
A题:
题目大意:给一个无向图,图中的边的权值会每小时变化一次,一天变化24次,然后给你k个询问,每个询问给你两个数D和S,问你在S时刻从节点1到节点D的最短路距离。
思路:第一题还是比较简单,直接建图跑最短路即可,因为每次都是从节点1出发,所以可以只做24次最短路,将每个时刻从1节点到其他的节点的最短距离算出来记录一下即可。
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <queue>
#define inf 2100000000
using namespace std;
int dist[510][24];
int cost[2010][24];
int ans[510][24];
struct edge{
int to,next,id;
}e[4010];
int box[510],cnt;
void init(){
memset(box,-1,sizeof(box));
cnt=0;
}
void add(int from,int to,int id){
e[cnt].to=to;
e[cnt].id=id;
e[cnt].next=box[from];
box[from]=cnt++;
}
struct node{
int u,t;
node(){
}
node(int a,int b){
u=a,t=b;
}
};
int vis[510][24];
void spfa(int time,int n){
for(int i=0;i<=n;i++){
for(int j=0;j<24;j++){
dist[i][j]=inf;
}
}
queue<node> q;
dist[1][time]=0;
memset(vis,0,sizeof(vis));
q.push(node(1,time));
vis[1][time]=1;
while(!q.empty()){
node now=q.front();q.pop();
int u=now.u,Time=now.t;
vis[u][Time]=0;
for(int t=box[u];t+1;t=e[t].next){
int v=e[t].to,id=e[t].id;
int nextt=(Time+cost[id][Time])%24;
if(dist[v][nextt]>dist[u][Time]+cost[id][Time]){
dist[v][nextt]=dist[u][Time]+cost[id][Time];
if(!vis[v][nextt]){
vis[v][nextt]=1;
q.push(node(v,nextt));
}
}
}
}
for(int i=1;i<=n;i++){
int mi=inf;
for(int j=0;j<24;j++){
mi=min(mi,dist[i][j]);
}
if(mi==inf)
mi=-1;
ans[i][time]=mi;
}
}
int main()
{
freopen("dd.in","r",stdin);
freopen("out.txt","w+",stdout);
int ncase,T=0;
scanf("%d",&ncase);
while(ncase--){
printf("Case #%d: ",++T);
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
init();
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y,i);
add(y,x,i);
for(int j=0;j<24;j++){
scanf("%d",&cost[i][j]);
}
}
for(int i=0;i<24;i++){
spfa(i,n);
}
while(k--){
int d,s;
scanf("%d%d",&d,&s);
printf("%d ",ans[d][s]);
}
printf("\n");
}
return 0;
}
B题:
题意:题目较长,看了半天,其实就是给你三个数组,A,B,C。(长度不超过2000),和两个数P,Q,问你是否可以从A,B,C中找出4个数,满足 A[x]∗B[y1]B[y2]∗C[z]=PQ
其中 y1≠y2 。
思路:比赛的时候比较着急,没想到太好的方法,我的写法是首先将 所有的 P∗C[z]Q∗A[x] 存到一个set中,这样复杂度是 O(Na∗Nclog(Na∗Nc)) ,然后再枚举所有的 B[y1]B[y2] ,看set中是否存在,如果存在则输出Yes,否则输出No。这一步复杂度是 O(Nb2∗log(Nb2)) 。以上 Na,Nb,Nc分别是数组A,B,C的长度。代码如下:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <queue>
#include <set>
#define inf 2100000000
#define ll long long
using namespace std;
struct node{
ll zi,mu;
node(){}
node(ll a,ll b){
zi=a,mu=b;
}
bool operator <(const node &ot)const{
if(zi==ot.zi){
return mu<ot.mu;
}
return zi<ot.zi;
}
};
int a[3][2010];
set<node> st;
int main()
{
freopen("dd.in","r",stdin);
freopen("out.txt","w+",stdout);
int ncase,T=0;
scanf("%d",&ncase);
while(ncase--){
st.clear();
printf("Case #%d:\n",++T);
scanf("%d%d%d",&a[0][0],&a[1][0],&a[2][0]);
for(int i=0;i<3;i++){
for(int j=1;j<=a[i][0];j++)
scanf("%d",&a[i][j]);
}
for(int i=1;i<=a[0][0];i++){
for(int j=1;j<=a[2][0];j++){
int x=a[0][i],y=a[2][j];
int gcd=__gcd(x,y);
x/=gcd;
y/=gcd;
//printf("%d %d\n",x,y);
st.insert(node(x,y));
}
}
int q;
scanf("%d",&q);
while(q--){
int P,Q,tru=0;
scanf("%d%d",&P,&Q);
for(int i=1;i<=a[1][0];i++){
for(int j=i+1;j<=a[1][0];j++){
ll x1=(ll)a[1][i]*P,y1=(ll)a[1][j]*Q;
ll gcd=__gcd(x1,y1);
x1/=gcd;
y1/=gcd;
ll x2=(ll)a[1][i]*Q,y2=(ll)a[1][j]*P;
gcd=__gcd(x2,y2);
x2/=gcd;
y2/=gcd;
if(st.count(node(x1,y1))!=0||st.count(node(y2,x2))!=0){
tru=1;
break;
}
}
if(tru)
break;
}
if(tru)
printf("Yes\n");
else
printf("No\n");
}
}
return 0;
}
C题:
题意:A,B两个人玩一个游戏,首先给一个数字N,A先手,两人轮流,如果轮到某个人的时候,N的各位上的数字之和是一个质数(1也算)。则此人失败,否则的话,它可以选择N的一个质因数m,将N除以m直到m不是N的质因数为止,然后轮到另一个人。现在给你这个数N,如果两人都使用最优策略,谁必胜。
思路:我记忆化搜索做的,首先将N分解质因数,因为N的范围是不大于 1015 ,其不同质因数个数不会很大。然后就是搜索,如果这个数的各位数之和是质数(包含1),则这个数先手必败。否则,查看这个数的后继状态(就是除掉一个质因数后得到的数),如果有一个是先手必败,那么这个数就是先手必胜,否则是先手必败。搜索过程中用一个map来记录结果。代码如下:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define inf 2100000000
#define maxn 40000000
#define ll long long
using namespace std;
map<ll,int> mp;
int p[maxn+10];
vector<ll> vec;
void init(){
memset(p,0,sizeof(p));
for(int i=2;i<=maxn;i++){
int x=i*2;
if(x>maxn)
break;
if(p[i])
continue;
while(x<=maxn){
p[x]=1;
x+=i;
}
}
for(int i=2;i<=maxn;i++){
if(!p[i])
vec.push_back(i);
}
}
bool ck(ll x){
int sum=0;
while(x){
sum+=x%10;
x/=10;
}
return !p[sum];
}
vector<ll> vis;
int use[30];
int dfs(ll x,int n){
int ret=mp[x];
if(ret)
return ret;
ret=-1;
if(ck(x))
return mp[x]=-1;
for(int i=0;i<n;i++){
if(!use[i]){
use[i]=1;
if(dfs(x/vis[i],n)==-1){
ret=1;
use[i]=0;
break;
}
use[i]=0;
}
}
return mp[x]=ret;
}
int main()
{
freopen("dd.in","r",stdin);
freopen("out.txt","w+",stdout);
init();
int ncase,T=0;
scanf("%d",&ncase);
while(ncase--){
printf("Case #%d: ",++T);
ll n,m;
cin>>n;
m=n;
int size=vec.size();
vis.clear();
memset(use,0,sizeof(use));
for(int i=0;i<size;i++){
if(m==1)break;
if(m%vec[i]==0){
ll ret=1;
while((m%vec[i])==0){
ret*=vec[i];
m/=vec[i];
}
vis.push_back(ret);
}
}
if(m!=1)
vis.push_back(m);
if(dfs(n,vis.size())==1)
printf("Laurence\n");
else
printf("Seymour\n");
}
return 0;
}
D题:
题意:给你一个只含有abcd四个字符的字符串S。然后称一个序列是合法的Albocede DNA序列当且仅当其满足下列4个条件:
1:至少含有abcd四种字符
2:所有的a在b的前面,所有的b在c的前面,所有的c在d的前面
3:a和c的数量相等
4:b和d的数量相等
现在称一个序列是Albocede-n DNA序列,只要它是一个或者多个Albocede DNA序列拼接而成:
如 abcdaabbbccddd。
求S有多少个子序列(注意不是子串),是Albocede-n DNA序列。这里不同位置得到的子序列当做是不同的。比如aabcd满足要求的子序列有两个。它们都是 abcd。具体可以参考样例。
思路:比赛时着急一开始没看清楚题,搞得死都过不了第四个样例。。。。
这道题可以用动态规划解决:
首先设4个状态:
状态0表示这样的字符串:Xa..ab..bc..cd..d 其中X是Albocede-n DNA序列(可为空),其中a的数量和c的数量一样,d的数量不能唱过b的数量,且大于0(以d结尾)
状态1表示这样的字符串:Xa..a 其中X和上面一样,a的数量大于0(以a结尾)。
状态2表示这样的字符串:Xa..ab..b 其中X和上面定义一样,a的数量和b的数量均大于0(以b结尾)
状态3表示这样的字符串:Xa..ab..bc..c其中 X定义和上面一样,a,b,c的数量均大于0(以c结尾),并且c的数量不能大于a的数量。
有了这三个状态,我们设 dp[i][state][n1][n2]表示S的前i个字符组成的字符串中(就是前缀i),满足状态state,并且a的数量与c的数量之差为n1,b和d的数量之差为n2的子序列的数量。(有点拗口哈。。。)。
那么更新答案就可在 dp[i-1][0][0][1]转移到dp[i][0][0][0]的时候进行。
状态转移就很明显了。
根据S[i]的字符,是a的话只会影响状态0,1的值,b只会影响状态1,2的值,c只会影响状态2,3的值,d只会影响状态3,0的值。具体n1,n2的变化可以根据状态的定义进行变化,具体可以参考代码实现。另外为了节约内存,可以使用滚动数组。代码如下
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define inf 2100000000
#define mod 1000000007
#define ll long long
using namespace std;
ll dp[2][4][255][255];
char str[510];
void add(ll &x,ll y){
x=(x+y)%mod;
}
int main(){
freopen("dd.in","r",stdin);
freopen("out.txt","w+",stdout);
int ncase,T=0;
scanf("%d",&ncase);
while(ncase--){
printf("Case #%d: ",++T);
scanf("%s",str+1);
int n=strlen(str+1),t=1;
memset(dp,0,sizeof(dp));
dp[0][0][0][0]=1;
ll ans=0;
for(int i=1;i<=n;i++){
memcpy(dp[t], dp[1-t], sizeof(dp[t]));
for(int state=0;state<4;state++){//a-cÊýÁ¿
for(int j=0;j<=250;j++){//b-dÊýÁ¿
for(int k=0;k<=250;k++){
if(dp[1-t][state][j][k]==0)
continue;
ll tmp=dp[1-t][state][j][k];
if(str[i]=='a'){
if(state==0){
if(j==0&&k==0){
add(dp[t][1][1][0],tmp);
}
}
else if(state==1){
if(k==0)
add(dp[t][1][j+1][0],tmp);
}
}
else if(str[i]=='b'){
if(state==1){
if(j>0&&k==0)
add(dp[t][2][j][1],tmp);
}
else if(state==2){
if(j>0)
add(dp[t][2][j][k+1],tmp);
}
}
else if(str[i]=='c'){
if(state==2){
if(j>0)
add(dp[t][3][j-1][k],tmp);
}
else if(state==3){
if(j>0)
add(dp[t][3][j-1][k],tmp);
}
}
else {
if(state==3){
if(j==0&&k>0){
add(dp[t][0][0][k-1],tmp);
if(k==1){
add(ans,tmp);
}
}
}
else if(state==0){
if(j==0&&k>0){
add(dp[t][0][0][k-1],tmp);
if(k==1)
add(ans,tmp);
}
}
}
}
}
}
t=1-t;
}
cout<<ans<<endl;
}
return 0;
}