美团笔试-3.18

1、捕获敌人

小美在玩一项游戏。该游戏的目标是尽可能抓获敌人。
敌人的位置将被一个二维坐标 (x, y) 所描述。
小美有一个全屏技能,该技能能一次性将若干敌人一次性捕获。
捕获的敌人之间的横坐标的最大差值不能大于A,纵坐标的最大差值不能大于B。
现在给出所有敌人的坐标,你的任务是计算小美一次性最多能使用技能捕获多少敌人。
输入描述
第一行三个整数N,A,B,表示共有N个敌人,小美的全屏技能的参数A和参数B。
接下来N行,每行两个数字x,y,描述一个敌人所在的坐标。
1 ≤ N ≤ 500,1 ≤ A , B ≤ 1000,1 ≤ x , y ≤ 1000
输出描述
一行,一个整数表示小美使用技能单次所可以捕获的最多数量。
样例输入
3 1 1
1 1
1 2
1 3
样例输出
2

实现(55%)

本题相当于给定一个矩形区域,需要使用矩形区域框出最多数量的敌人。考虑使用循环遍历每一个节点,而后再次进行二重循环遍历剩余的每一个节点,我们需要确保一开始的节点为当前举行区域的左上角,而后统计当前区域中的敌人个数。

总结

考虑到数据量不大,可以直接构建二维矩阵用于记录每个坐标上是否有敌人,最终只需要遍历二维矩阵寻找在一个矩形区域中能够获得敌人的最大数量即可。值得注意的是,可能会出现多个敌人有着相同坐标的情况,因此不能单纯将矩阵的值设置为 1 。同时为了方便求解区间和,可以使用前缀和进行进一步优化。

int g[N][N];

int main() {
    int n, a, b;
    cin >> n >> a >> b;
    for (int i = 1; i <= n; i++) {
        int x, y;
        cin >> x >> y;
        g[x][y]++;
    }
    for (int i = 1; i <= 1000; i++) {
        for (int j = 1; j <= 1000; j++) {
            g[i][j] += g[i - 1][j] + g[i][j - 1] - g[i - 1][j - 1];
        }
    }
    int ans = 0;
    for (int i = 1; i <= 1000; i++) {
        for (int j = 1; j <= 1000; j++) {
            ans = max(ans, g[i][j] - g[max(0, i - a - 1)][j] - g[i][max(0, j - b - 1)] + g[max(0, i - a - 1)][max(0, j - b - 1)]);
        }
    }
    cout << ans << endl;
    return 0;
}

2、彩带

题目描述:
小美现在有一串彩带,假定每一厘米的彩带上都是一种色彩。
因为任务的需要,小美希望从彩带上截取一段,使得彩带中的颜色数量不超过K种。
显然,这样的截取方法可能非常多。于是小美决定尽量长地截取一段。
你的任务是帮助小美截取尽量长的一段,使得这段彩带上不同的色彩数量不超过K种。
输入描述
第一行两个整数N,K,以空格分开,分别表示彩带有N厘米长,你截取的一段连续的彩带不能超过K种颜色。
接下来一行N个整数,每个整数表示一种色彩,相同的整数表示相同的色彩。
1 ≤ N , K ≤ 5000,彩带上的颜色数字介于 [ 1 , 2000 ] 之间。
输出描述
一行,一个整数,表示选取的彩带的最大长度。
样例输入
8 3
1 2 3 2 1 4 5 1
样例输出
5

实现(AC)

利用数组记录彩带上的每一种颜色,利用哈希集合控制彩带的类型以及数量。我们一次遍历每一个节点,探索从他开始最多能访问几种颜色。在探索过程中利用哈希集合判断当前颜色是否以获得并及时更新哈希表的大小,若哈希表大小超出范围则立刻终止。最终计算并更新当前的最大长度。

int main() {
    int n, k, res = 0;
    scanf("%d%d", &n, &k);
    vector<int> line(n);
    for (int i = 0; i < n; ++i) {
        int temp;
        scanf("%d", &temp);
        line[i] = temp;
    }
    unordered_set<int> hs;
    for (int i = 0; i < n; ++i) {
        int j = i;
        for (; j < n; ++j) {
            if (!hs.count(line[j])) {
                hs.insert(line[j]);
            }
            if (hs.size() > k) {
                break;
            }
        }
        res = max(res, j - i);
        hs.clear();
    }
    printf("%d", res);
}

3、回文串

题目描述:
现在小美获得了一个字符串。小美想要使得这个字符串是回文串。
小美找到了你。你可以将字符串中至多两个位置改为任意小写英文字符 ’a’ - ‘z’
你的任务是帮助小美在当前制约下,获得字典序最小的回文字符串。
数据保证能在题目限制下形成回文字符串。
注:回文字符串:即一个字符串从前向后和从后向前是完全一致的字符串。
例如字符串 abcba , aaaa, acca 都是回文字符串。字符串 abcd , acea 都不是回文字符串。
输入描述
一行,一个字符串。字符串中仅由小写英文字符构成。
保证字符串不会是空字符串。
字符串长度介于 [ 1 , 100000 ] 之间。
输出描述
一行,一个在题目条件限制下所可以获得的字典序最小的回文字符串。
样例输入
acca
样例输出
aaaa

实现(AC)

考虑到题目要求最多修改两个字符就能让当前字符串变成回文子串,且要求返回的回文子串应当是字典顺序最小的。我们可以使用双指针找到对应位置不相等的序号并记录下来,而后进行讨论:

  1. 数组大小为 0 ,所有位置都对应相等。此时我们应当使用修改两个字符的名额用于优化字符串的字典序。我们移动双指针,将最接近开头的对应位置上不为 ‘a’ 的字符修改为 ‘a’ 。
  2. 数组大小为 2 。值得注意的是,此时可能出现两种情况:1、有一对字符不相等且两个字符军不等于 ‘a’ ,为了字典序最小此时我们需要将两个字符都修改为 ‘a’ ;2、有一对字符不相等且有一个字符军等于 ‘a’ ,为了字典序最小此时我们需要其对应的字符都修改为 ‘a’ ,且为了不浪费修改名额,当字符串长度为奇数时我们还可以将最中间元素修改为 ‘a’ 。
  3. 数组大小为 4 ,有两堆字符不相等。我们将对应的两对字符取各自的最小值。
int main() {
    string s;
    cin >> s;
    int low = 0, high = s.size() - 1, diff = 0;
    vector<int> it;
    while (low < high) {
        if (s[low] != s[high]) {
            ++diff;
            it.emplace_back(low);
            it.emplace_back(high);
        }
        ++low;
        --high;
    }
    low = 0;
    high = s.size() - 1;
    if (it.empty()) {
        while (s[low] == 'a') {
            ++low;
            --high;
        }
        s[low] = 'a';
        s[high] = 'a';
    } else if (it.size() == 2) {
        if (s.size() % 2 && (s[it[0]] == 'a' || s[it[0]] == 'a')){
            s[s.size() / 2] = 'a';
        }
        s[it[0]] = 'a';
        s[it[1]] = 'a';
    } else {
        s[it[0]] = min(s[it[0]], s[it[1]]);
        s[it[1]] = min(s[it[0]], s[it[1]]);
        s[it[2]] = min(s[it[2]], s[it[3]]);
        s[it[3]] = min(s[it[2]], s[it[3]]);
    }

    cout << s;
}

4、商店

题目描述:
现在商店里有N个物品,每个物品有原价和折扣价。
小美想要购买商品。小美拥有X元,一共Y张折扣券。
小美需要最大化购买商品的数量,并在所购商品数量尽量多的前提下,尽量减少花费。
你的任务是帮助小美求出最优情况下的商品购买数量和花费的钱数。
输入描述
第一行三个整数,以空格分开,分别表示N,X,Y。
接下来N行,每行两个整数,以空格分开,表示一个的原价和折扣价。
1≤N≤100, 1≤X≤5000, 1≤Y≤50,每个商品原价和折扣价均介于[1,50]之间。
输出描述
一行,两个整数,以空格分开。第一个数字表示最多买几个商品,第二个数字表示在满足商品尽量多的前提下所花费的最少的钱数。
样例输入
3 5 1
4 3
3 1
6 5
样例输出
2 5

实现(9%)

应该是 01 背包的变形,需要使用动态规划。而且考虑到优惠券,还存在一个原始价格到优惠价格之间的映射,没写完。代码参考。

int a[N],b[N];
int dp[105][5005][55],cost[105][5005][55];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int n, x, y;
    cin >> n >> x >> y;
    for (int i = 1; i <= n; i++)
        cin >> a[i] >> b[i];

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= x; j++) {
            for (int k = 0; k <= y; k++) {
                dp[i][j][k] = dp[i - 1][j][k];
                cost[i][j][k] = cost[i - 1][j][k];
                if (j - a[i] >= 0) {
                    if (1 + dp[i - 1][j - a[i]][k] > dp[i][j][k]) {
                        dp[i][j][k] = 1 + dp[i - 1][j - a[i]][k];
                        cost[i][j][k] = cost[i - 1][j - a[i]][k] + a[i];
                    } else if (1 + dp[i - 1][j - a[i]][k] == dp[i][j][k])
                        cost[i][j][k] = min(cost[i][j][k], cost[i - 1][j - a[i]][k] + a[i]);
                }
                if (j - b[i] >= 0 && k >= 1) {
                    if (1 + dp[i - 1][j - b[i]][k - 1] > dp[i][j][k]) {
                        dp[i][j][k] = 1 + dp[i - 1][j - b[i]][k - 1];
                        cost[i][j][k] = cost[i - 1][j - b[i]][k - 1] + b[i];
                    } else if (1 + dp[i - 1][j - b[i]][k - 1] == dp[i][j][k])
                        cost[i][j][k] = min(cost[i][j][k], cost[i - 1][j - b[i]][k - 1] + b[i]);
                }
            }
        }
    }
    int mn = 1e9;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= x; j++) {
            for (int k = 0; k <= y; k++) {
                //cout<
                if (dp[i][j][k] == dp[n][x][y])
                    mn = min(mn, cost[i][j][k]);
            }
        }
    }
    cout << dp[n][x][y] << " " << mn << endl;
    return 0;
}

你可能感兴趣的:(笔试总结,算法,数据结构,蓝桥杯)