H Rice Arrangement
题意
有一个有 n n n个座位的桌子,有 k k k个人和 k k k盘菜,通过旋转桌子让每个人都吃且仅吃一道菜问最小旋转角度。
思路
首先引入一个结论
把每个人和对应的手抓饭在圆盘上连线,这些连线是不会相交的
然后设置k层轮转,选择第一个人的菜
然后对于每一个轮转,上面的每一道菜可以分为两类:
- 通过逆时针旋转到对应的人的口中
- 通过顺时针寻转到对应的人的口中
每一种轮转的最小答案值就是 m i n ( 2 ∗ m a x C o s t 顺 + m a x C o s t 逆 , m a x C o s t 逆 + 2 ∗ m a x C o s t 顺 ) min(2*maxCost_{顺}+maxCost_{逆},maxCost_{逆}+2*maxCost_{顺}) min(2∗maxCost顺+maxCost逆,maxCost逆+2∗maxCost顺)的最小值
然后贪心考虑,如果选择了最大的顺时针寻转角度为 x x x,那么所有角度在此之前的可以被“免代价”选择,逆时针同理
然后考虑到,除了 C o s t = 0 Cost=0 Cost=0的情况,别的情况下每个点的 C o s t 顺 + C o s t 逆 = k Cost_{顺}+Cost_{逆}=k Cost顺+Cost逆=k
所以我们可以按 C o s t 顺 Cost_{顺} Cost顺来排序,然后枚举分界点计算答案
代码
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
#define P pair<ll, ll>
int a[maxn], b[maxn];
P dis[maxn];
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= k; ++i) {
scanf("%d", a + i);
}
for (int i = 1; i <= k; ++i) {
scanf("%d", b + i);
}
sort(a + 1, a + 1 + k);
sort(b + 1, b + 1 + k);
ll ans = 0x3f3f3f3f3f3f3f3f;
for (int delta = 0; delta < k; ++delta) {
ll tpans = 0x3f3f3f3f3f3f3f3f;
ll mx = 0;
for (int i = 1; i <= k; ++i) {
dis[i].first = (a[i] - b[(i + delta) % k + 1] + n) % n;
dis[i].second = n-dis[i].first;
mx = max(mx, dis[i].second);
}
tpans = min(tpans, mx);
sort(dis + 1, dis + 1 + k);
dis[k + 1].first = dis[k + 1].second = 0;
for (int i = 1; i <= k; ++i) {
tpans = min({tpans, dis[i].first * 2 + dis[i + 1].second,
dis[i].first + dis[i + 1].second * 2});
}
ans = min(ans, tpans);
}
printf("%lld\n", ans);
}
return 0;
}