【kick start】2019-round A

目录

1-培训队员-medium。排序、前缀和?

2-物流中心选址-hard。BFS、二分法

3-电影片订座位-hard。贪心、线段树


 

1-培训队员-medium。排序、前缀和?

题意:从N个人里选P个人,每个人有自己的技能值,选定P个人之后把所有人fulfill成一样的技能值对应的fulfill代价,求最小

You have decided that a team is fair if it has exactly P students on it and they all have the same skill rating. That way, everyone plays as a team. Initially, it might not be possible to pick a fair team, so you will give some of the students one-on-one coaching. It takes one hour of coaching to increase the skill rating of any student by 1.

The competition season is starting very soon (in fact, the first match has already started!), so you'd like to find the minimum number of hours of coaching you need to give before you are able to pick a fair team.

从N个人里面选P个人,每个人有技能值,选出来的P个人所有人向最高技能值看齐,求最小代价。一开始往硬币组合的DP思路去想了,没想出来,原来是用排序做,对数组排序,因为部分顺序,所以取出来的P个人必然是有一个从低到高的顺序,不如先排了,然后用数组存储前缀和,一组一组取,N个人一共可取N-P组,在这N-P组中选择最小代价的

def solve(N, P, S):
    cum_sum = [0] * N
    cum_sum[0] = S[0]
    for i in range(1, N):
        cum_sum[i] = cum_sum[i-1] + S[i]
    res = S[P-1] * P - cum_sum[P-1]
    for i in range(N):
        if i + P < N:
            res = min(res, S[i+P] * P - (cum_sum[i+P] - cum_sum[i]))
        else:
            break
    return res
        


def test():
    T = int(input().strip())
    for i in range(T):
        N, P = list(map(int, input().strip().split()))
        S = list(map(int, input().strip().split()))
        S.sort()
        res = solve(N, P, S)
        out = "Case #%d: %s\n" % (i+1, res)
        print(out)
test()

2-物流中心选址-hard。BFS、二分法

题意:在网格中选一处空地作为新的物流站点,求选中某处最优位置后,所有空地到最近站点的最大值最小,也就是降低物流成本

The world can be divided into an R × C grid of squares. Each square either contains a delivery office or it does not. You may pick a grid square that does not already contain a delivery office and build a new delivery office there.

The delivery time of a parcel to a square is 0 if that square contains a delivery office. Otherwise, it is defined as the minimum Manhattan distance between that square and any other square containing a delivery office. The overall delivery time is the maximum of delivery times of all the squares. What is the minimum overall delivery time you can obtain by building at most one new delivery office?

没有想到是二分法这种暴力解法,O(n^2logn),对于python来说是件复杂度依然很大。思路就是从0-R+C二分,判断所有空地到达最近站点的最大距离不超过mid,不超过的话继续向左收敛,反之向右收敛。基本步骤是给定阈值K,求所有K步之内能到的空地打好标记,对于K步之内不能到的空地计算好i+j和i-j的最大最小值,为后续求与站点的距离做准备,然后对每个空地,认为可以作为站点的情况下能否在K内cover住这个最大的距离,能就代表此处可以设为站点。很绕,看代码。

这里面涉及到一个知识点,就是求曼哈顿距离的公式,利用此公式做曼哈顿距离的拆解,等价于求另一部分的最大值

we note that the manhattan distance has an equivalent formula:

dist((x1, y1), (x2, y2)) = max(abs(x1 + y1 - (x2 + y2)), abs(x1 - y1 - (x2 - y2)))

def solve(R, C, grid):
    """
    二分搜索K值,求出新站点设在某处后,所有空地到达最近站点的最大值能最小
    """
    l = 0
    r = R + C
    while l < r:
        mid = l + (r-l)//2
        if valid(mid, grid, R, C):
            r = mid
        else:
            l = mid + 1
    return l

def valid(k, grid, R, C):
    """
    判断这个k值是否能保证在某处设了新站点后,所有空地都能在k步内到最近的站点
    1. 用最近距离标记k步内能到的空地
    2. k步内不能到的这部分空地,求曼哈顿距离的一半的最大
    3. 将任一空地作为新站点,求新站点与最大曼哈顿距离一半构成的曼哈顿距离,若这个距离在k内,
       说明在此处新建站点可以在k步内cover住所有空地
    """
    # step 1
    dist = [[-1 for _ in range(C)] for _ in range(R)]
    q = []
    max_add = float('-inf')
    min_add = float('inf')
    max_sub = float('-inf')
    min_sub = float('inf')
    for i in range(R):
        for j in range(C):
            if grid[i][j] == 1:
                dist[i][j] = 0
                q.append((i, j))
                
    around = [[-1, 0], [1, 0], [0, -1], [0, 1]]
    while q:
        i, j = q.pop(0)
        distance = dist[i][j]
        if distance == k:
            continue
        for d in around:
            n_i = i + d[0]
            n_j = j + d[1]
            if n_i >= 0 and n_i < R and n_j >= 0 and n_j < C and dist[n_i][n_j] == -1:
                dist[n_i][n_j] = distance + 1
                q.append((n_i, n_j))
    # step 2
    for i in range(R):
        for j in range(C):
            if dist[i][j] == -1:
                max_add = max(max_add, i+j)
                min_add = min(min_add, i+j)
                max_sub = max(max_sub, i-j)
                min_sub = min(min_sub, i-j)
    if min_add == float('inf'):
        return True

    # step 3
    for i in range(R):
        for j in range(C):
            if grid[i][j] == 0:
                max_sum1 = max(abs(max_add - (i+j)), abs((i+j)-min_add))
                max_sum2 = max(abs(max_sub - (i-j)), abs((i-j)-min_sub))
                if max(max_sum1, max_sum2) <= k:
                    return True
    return False

def test():
    T = int(input().strip())
    for i in range(T):
        R, C = [int(i) for i in input().strip().split() if i != ' ']
        grid = []
        for _ in range(R):
            cur_row = [int(i) for i in list(input().strip()) if i != ' ']
            grid.append(cur_row)
        res = solve(R, C, grid)
        out = "Case #%d: %s\n" % (i + 1, res)
        print(out)
test()

3-电影片订座位-hard。贪心、线段树

题意:有N个座位,Q个订单,让每个订单尽量订到尽量多的座位,求每个订单订到坐位中的最小值,让这个最小值最大

Since some of the bookings may overlap, the system might not be able to fulfill each booking entirely. When you enter a booking into the system, it will assign every seat requested by the booking that hasn't already been assigned to a booking entered into the system earlier.

What is the largest integer k where there exists an order that you can enter the bookings into the system, such that each booking is assigned at least k seats?

 通过合理的安排这些订单被fulfill的先后顺序来控制求最优解。问题就归结成了按照某个策略对Q排序,然后按照排序后的结果处理这些订单,计算被fulfill的最小值,这个最小值就是最优值。策略就是按照每个订单未被其他所有订单覆盖的座位数量,先选出最大的最后处理,再对剩下订单按照这个策略重新计算,选出最大的放在之前已确定的订单前面,直到只剩一个订单。

不太会写啊,就算get了思路。。。

// In the name of God

#include <iostream>
#include <algorithm>
#include <fstream>
#include <vector>
#include <deque>
#include <assert.h>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <stdio.h>
#include <string.h>
#include <utility>
#include <math.h>
#include <bitset>
#include <iomanip>
#include <complex>

using namespace std;

#define rep(i, a, b) for (int i = (a), i##_end_ = (b); i < i##_end_; ++i)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define mp make_pair
#define x first
#define y second
#define pb push_back
#define SZ(x) (int((x).size()))
#define ALL(x) (x).begin(), (x).end()

template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
template<typename T> inline bool smin(T &a, const T &b)   { return a > b ? a = b : a;    }
template<typename T> inline bool smax(T &a, const T &b)   { return a < b ? a = b : a;    }

typedef long long LL;

const int N = (int) 1e6 + 6, mod = (int) 0;
int n, q, xl[N], xr[N], mvl[N];
pair<int, int> sr[N];
int check(int k) {
	for (int j = 0; j < q; ++j)
		xl[j] = sr[j].first, xr[j] = -sr[j].second, mvl[j] = xl[j];
	for (int j = 0; j < q; ++j) {
		int l = xl[j], r = xr[j];	
		int st = mvl[j];
		int allowed_after = r;
		int cnt = 0;
		for (int i = j + 1; i < q; ++i) {
			if (xr[i] <= r) {
				if (xl[i] <= st) {
					st = max(st, xr[i]);	
				} else {
					cnt += xl[i] - st;
					st = max(st, xr[i]);
					if (cnt >= k) {
						allowed_after = xl[i] - (cnt - k);
						break;
					}
				}
			}
		}
		if (cnt < k) {
			cnt += r - st;
			if (cnt < k) return 0;
			allowed_after = r - (cnt - k);
		}
		for (int i = j + 1; i < q; ++i) {
			if (xl[i] >= allowed_after) break;
			if (xr[i] > r) {
				mvl[i] = max(mvl[i], r);
			}
		}
	}
	return 1;
}
int main() {
	int tc;
	cin >> tc;
	for (int tt = 1; tt <= tc; ++tt) {
		cout << "Case #" << tt << ": ";
		cin >> n >> q;
		for (int j = 0; j < q; ++j) {
			cin >> xl[j] >> xr[j], --xl[j];
			sr[j] = mp(xl[j], -xr[j]);	
		}
		sort(sr, sr + q);
		int bl = 0, br = n + 1;
		while (bl < br - 1) {
			int bm = bl + br >> 1;
			if (check(bm)) {
				bl = bm;
			} else {
				br = bm;
			}
		}
		cout << bl << '\n';

	}
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值