题目链接:http://codeforces.com/contest/1283/problem/D
题意:给你n棵树在数轴上的坐标。有m个人,要你把这m个人放在数轴上,一个点只能放一个人(树所在的点不能放)。要使得这m个人每个人到离他最近的树的距离(以下统称距离)的总和最小,输出这个最小值以及m个人在数轴上的坐标。
解法一:BFS
我看过网上大部分人思路是跑BFS,因为假设树坐标是x,那我肯定是放 x+1,x-1,x+2,x-2,...这样最优的,所以我用map记录这个点有没有被放过,然后广搜的去放人就ok了。
AC代码
1 //#include<bits/stdc++.h> 2 #include<set> 3 #include<map> 4 #include<stack> 5 #include<cmath> 6 #include<ctime> 7 #include<queue> 8 #include<cstdio> 9 #include<string> 10 #include<vector> 11 #include<cstdlib> 12 #include<cstring> 13 #include<iostream> 14 #include<algorithm> 15 #include<unordered_map> 16 using namespace std; 17 typedef long long ll; 18 typedef unsigned long long ull; 19 typedef pair<int, int> pii; 20 typedef pair<ll, ll> pll; 21 typedef pair<ll, int> pli; 22 typedef pair<int, ll> pil; 23 #define X first 24 #define Y second 25 #define pb push_back 26 inline ll gcd(ll a, ll b) { while (b != 0) { ll c = a % b; a = b; b = c; }return a < 0 ? -a : a; } 27 inline ll lcm(ll a, ll b) { return (a * b) / gcd(a, b); } 28 inline ll lowbit(ll x) { return x & (-x); } 29 const double PI = 3.14159265358979323846; 30 const int inf = 0x3f3f3f3f; 31 const ll INF = 0x3f3f3f3f3f3f3f3f; 32 const ll mod = 998244353; 33 inline char nc() { 34 static char buf[1 << 21], * p1 = buf, * p2 = buf; 35 return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++; 36 } 37 inline ll rd() { 38 //#define getchar nc 39 ll x = 0, f = 1; char ch = getchar(); 40 while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); } 41 while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); } 42 return x * f; 43 } 44 const double eps = 1e-8; 45 const int M = 2e5 + 10; 46 const int N = 1e6 + 10; 47 int a[N]; 48 int n, m; 49 queue<pii>q; 50 map<int, bool>vis; 51 ll ans = 0; 52 vector<int>ver; 53 void bfs() { 54 while (m > 0) { 55 pii st = q.front(); q.pop(); 56 if (!vis.count(st.X - 1)) { 57 ans += 1ll * st.Y + 1; 58 q.push({ st.X - 1,st.Y + 1 }); 59 vis[st.X - 1] = 1; 60 ver.pb(st.X - 1); 61 m--; 62 } 63 if (m == 0)break; 64 if (!vis.count(st.X + 1)) { 65 ans += 1ll * st.Y + 1; 66 q.push({ st.X + 1,st.Y + 1 }); 67 vis[st.X + 1] = 1; 68 ver.pb(st.X + 1); 69 m--; 70 } 71 } 72 } 73 int main() { 74 n = rd(), m = rd(); 75 for (int i = 1; i <= n; i++) { 76 a[i] = rd(); 77 vis[a[i]] = 1; 78 q.push({ a[i],0 }); 79 } 80 bfs(); 81 printf("%lld\n", ans); 82 for (auto& val : ver) { 83 printf("%d ", val); 84 } 85 puts(""); 86 }
解法二: 二分+模拟
先说这个做法的代码实现的确较烦一点,但是也不失是一种以后值得运用的思维方式。我们考虑距离最大的人的值是多少,因为试想如果距离最大的人确定下来了我们假设为x,那么对于这n课树,所有距离他小于x的并且能放人的点必定都会被放上人(贪心原理),然后我们还有 (m-已经放好的人数) 个人未放,这些人到树的距离必定都是x,所以接下来就相当于模拟题一样,把剩下的人放掉就好了。所以我们关键在于如何去确定这个最小的x是多少,换言之 距离最大的人最小是多少,对于这种最大值求最小的,我们显然可以用二分。我们先将n课树的坐标按升序进行排序。每次的check,我们贪心的去放这m个人就可以了。
而且最重要的是,这样写代码跑的速度比一种方法快了不只一倍(虽然复杂度是一样的),但是对于我这种十分看重代码速度的人来说,心里不要多舒服。^-^
AC代码
//#include<bits/stdc++.h> #include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<cstdio> #include<string> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #include<unordered_map> using namespace std; #define ll long long #define ull unsigned long long #define pii pair < int,int> #define pll pair < ll , ll > #define X first #define Y second inline ll gcd(ll a, ll b) { while (b != 0) { ll c = a % b; a = b; b = c; }return a < 0 ? -a : a; } inline ll lcm(ll a, ll b) { return (a * b) / gcd(a, b); } inline ll lowbit(ll x) { return x & (-x); } const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; const ll mod = 998244353; inline ll rd() { ll x = 0, f = 1; char ch = getchar(); while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); } return x * f; } const double eps = 1e-8; const int M = 4e6 + 10; const int N = 1e6 + 10; int a[N]; vector<int>mp[N]; int n, m; bool check(int x) { int ma = -inf; int sum = 0; for (int i = 1; i <= n; i++) { sum += min(a[i] - ma - 1, x); sum += min(a[i + 1] - a[i] - 1, x); ma = min(a[i + 1] - 1, a[i] + x); if (sum >= m)return 1; } return 0; } int main() { n = rd(), m = rd(); for (int i = 1; i <= n; i++)a[i] = rd(); sort(a + 1, a + 1 + n); a[0] = -inf; a[n + 1] = inf; int l = 1, r = m; while (l < r) { int mid = (l + r) >> 1; if (check(mid)) r = mid; else l = mid + 1; } int dis = l - 1; ll ans = 0; int ma = -inf; int cnt = 0; for (int i = 1; i <= n; i++) { for (int j = max(ma + 1, a[i] - dis); j <= a[i] - 1; j++) ans += abs(j - a[i]), cnt++, mp[i].push_back(j); for (int j = a[i] + 1; j <= min(a[i + 1] - 1, a[i] + dis); j++) ans += min(abs(j - a[i]), abs(j - a[i + 1])), cnt++, mp[i].push_back(j); ma = min(a[i + 1] - 1, a[i] + dis); } ans += ((ll)m - cnt) * ((ll)dis + 1); int res = m - cnt; for (int i = 1; i <= n; i++) { int x, l, r, y; if (mp[i - 1].size()) x = max(a[i - 1], mp[i - 1][mp[i - 1].size() - 1]); else x = a[i - 1]; if (mp[i].size()) l = min(mp[i][0] - 1, a[i] - 1), r = max(mp[i][mp[i].size() - 1] + 1, a[i] + 1); else l = a[i] - 1, r = a[i] + 1; if (mp[i + 1].size()) y = min(a[i + 1], mp[i + 1][0]); else y = a[i + 1]; if (l > x) res--, mp[i - 1].push_back(l); if (res == 0)break; if (r < y) res--, mp[i].push_back(r); if (res == 0)break; } printf("%lld\n", ans); for (int i = 0; i <= n; i++) { for (int j = 0; j < mp[i].size(); j++) { printf("%d ", mp[i][j]); } } printf("\n"); }