zoukankan      html  css  js  c++  java
  • Google 2020KS-RoundD D题题解

    前言

    感觉Google KickStart的后两题还是有一点难度的。。尤其是D题,很多时候打比赛的时候都A不掉。
    但其实看题解以后发现它也没考什么很难的算法,就只是需要好好思考一下orz
    害,说白了还是手生,这种稍微有点难度的题目还是得做一下

    题目描述

    题解

    麻烦点儿的广搜应该可以做到(O(n^2)),但是这个题的数据范围还是过不了。
    但无论是广搜还是暴力,它的思路都是将每个人的名字看成一个节点,然后有朋友关系的来连边。
    实际上,每个人的名字可以看成一个集合,朋友关系可以用集合运算表示。
    在集合运算中,每个元素对答案的贡献是互相独立的。类比位运算(|和&)操作。
    那么在这个题中,每个字母的作用都是相互独立的。完全可以只建立字母之间的关系。

    我自己就想到这一步,剩下的都是看官方题解做的。
    题解的做法是,将每个字母看成节点,如果两个字母在同一个名字里,就连边。
    然后跑一个floyd最短路。
    查询的时候,分别枚举两个人名字里的字母c1和c2,选择c1到c2长度最短的路径作为答案。
    当然输出的时候这个值要+2。
    (思路是这么个思路,不知道细节跟题解是不是一样)

    为什么这样建图是对的呢?考虑在这样一个图中走过了一条边代表着什么。
    比如上面的例子:ABC、CDE和DEF三个人。
    如果是ABC和CDE要联系,那么根本就不需要走边(因为它们有相同的字母C,最短路径是0,最后输出的答案是0+2=2)
    而ABC和DEF要联系,需要走C-E这条边。这意味着它们将CDE这个人当做了中间桥梁,而CDE这个人贡献出的联系就是C-E。
    则最短路径是1,加上两端的ABC和DEF,答案是3。

    这也就是说,在这个图中,每次走过一条边,就说明中间的路径上多了一个人,这个人提供了一个字母的转换。
    最短路径求出来就是中间最少经过几个人,然后加上两边的人就是答案。

    代码

    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int T, n, q;
    char name[50010][30];
    int f[30][30];
    void addPath(char* str) {
    	int len = strlen(str);
    	for (int i = 0; i < len; i ++)
    		for (int j = 0; j < len; j ++)
    			f[str[i] - 'A' + 1][str[j] - 'A' + 1] = 1;
    }
    int getAnswer(char* s1, char* s2) {
    	int l1 = strlen(s1), l2 = strlen(s2);
    	int ans = n + 1;
    	for (int i = 0; i < l1; i ++)
    		for (int j = 0; j < l2; j ++)
    			ans = min(ans, f[s1[i] - 'A' + 1][s2[j] - 'A' + 1]);
    	if (ans == n + 1) ans = -1;
    	else ans += 2;
    	return ans;
    }
    int main()
    {
    	scanf("%d", &T);
    	for (int wer = 1; wer <= T; wer ++) {
    		scanf("%d%d", &n, &q);
    		memset(f, 0x3f, sizeof(f));
    		for (int i = 1; i <= n; i ++) {
    			scanf("%s", name[i]);
    			addPath(name[i]);
    		}
    		for (int i = 1; i <= 26; i ++)
    			f[i][i] = 0;
    		for (int k = 1; k <= 26; k ++)
    			for (int i = 1; i <= 26; i ++)
    				for (int j = 1; j <= 26; j ++)
    					f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
    
    		printf("Case #%d:", wer);
    		for (int i = 1; i <= q; i ++) {
    			int x, y;
    			scanf("%d%d", &x, &y);
    			printf(" %d", getAnswer(name[x], name[y]));
    		}
    		printf("
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    java实现遍历树形菜单方法——service层
    Es 中一个分片一般设置多大
    Too Many Open Files的错误
    线程池队列满导致错误
    ES正在弱化type这个概念
    更新设置api
    遥控器 静音键 点播键
    Byzantine failures
    TGI指数
    墨菲定律(Murphy's Law)
  • 原文地址:https://www.cnblogs.com/FromATP/p/14588120.html
Copyright © 2011-2022 走看看