Banquet Preparations 1
[Link](Problem - G - Codeforces)
题意
给你 n n n组菜品每次包括 a i , b i a_i,bi ai,bi,每组菜品你必须吃 m m m,问你吃完以后 ∣ ∑ a − ∑ b ∣ |\sum a-\sum b| ∣∑a−∑b∣最小是多少。
思路
首先考虑不变的是 ∑ a + ∑ b − n × m \sum a+\sum b -n\times m ∑a+∑b−n×m,我们希望操作完 ∑ a = ∑ a + ∑ b − n × m 2 , ∑ b = ∑ a + ∑ b − n × m 2 \sum a=\frac {\sum a+\sum b -n\times m}{2},\sum b=\frac {\sum a+\sum b -n\times m}{2} ∑a=2∑a+∑b−n×m,∑b=2∑a+∑b−n×m,这样一定是最优的因为是答案是 0 0 0。那么我们怎么让 ∑ a , ∑ b \sum a,\sum b ∑a,∑b迫近这个值呢,我们可以先预处理出来 a a a的最小操作次数即最少被吃多少 s u m a suma suma,如果 s u m a < ∑ a + ∑ b − n × m 2 suma<\frac {\sum a+\sum b -n\times m}{2} suma<2∑a+∑b−n×m,则 a a a再怎么操作都不能迫近了,否则我们一次枚举每一个 a i a_i ai,看看现在 a i a_i ai能吃多少,那么受三个条件限制,1.不能超过迫近的距离 2.不能超过吃超过 a i a_i ai 3.不能超过吃大于 m m m
最后输出即可
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#include <deque>
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
LL a[N], b[N], c[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n >> m;
for (int i = 1; i <= n; i ++) cin >> a[i] >> b[i];
LL sum = 0, suma = 0, tot = 0;
for (int i = 1; i <= n; i ++) {
sum += a[i] + b[i] - m;
c[i] = max(0ll, m - b[i]);
tot += c[i];
suma += a[i];
}
if (suma - tot > sum / 2) {
sum -= sum / 2;
for (int i = 1; i <= n; i ++) {
LL x = min(suma - tot - sum, a[i] - c[i]);
x = min(x, m - c[i]);
c[i] += x;
tot += x;
}
}
LL sa = 0, sb = 0;
for (int i = 1; i <= n; i ++)
sa += a[i] - c[i], sb += b[i] - (m - c[i]);
cout << abs(sa - sb) << endl;
for (int i = 1; i <= n; i ++)
cout << c[i] << ' ' << m - c[i] << endl;
}
return 0;
}
Banquet Preparations 2
[Link](Problem - H - Codeforces)
题意
给你 n n n个 a i , b i , m i a_i,b_i,m_i ai,bi,mi,每个 a i , b i a_i,b_i ai,bi一共吃 m i m_i mi问你吃完后最少有多少个不同的 a i , b i {a_i,b_i} ai,bi。
思路
首先如果 a i + b i − m i a_i+b_i-m_i ai+bi−mi不一样的话一定不能吃成一样的,所以我们先按 a i + b i − m i a_i+b_i-m_i ai+bi−mi分组,对于每一组发现 a i a_i ai操作完以后的所有可行解是一个连续的区间,即 [ m a x ( 0 , a i − m ) , a i − m a x ( 0 , m − b i ) ] [max(0,a_i-m),a_i-max(0,m-b_i)] [max(0,ai−m),ai−max(0,m−bi)],相当于一个区间 [ l i , r i ] [l_i,r_i] [li,ri],因为他们操作完的和相同所以操作完 a i a_i ai相同那么 b i b_i bi一定相同,所以我们只关注 a i a_i ai即可。
发现对于同一组就是一些区间,我们要选取最少的点使得每个区间内至少包含一个点,我们仿照区间合并的做法,对于某个区间我们将点放在最右边一定更优因为这样可以覆盖到更多后面的点,因此将所有区间按照右端点从小到大排序,贪心做区间选点即可。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#include <deque>
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n;
struct Node {
int id, aa, bb, m, l, r, len;
}a[N];
bool cmp(Node a, Node b) {
if (a.len ^ b.len) return a.len < b.len;
if (a.r ^ b.r) return a.r < b.r;
return a.l < b.l;
}
bool cmp2(Node a, Node b) {
return a.id < b.id;
}
int p[N], cnt;
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n;
cnt = 0;
for (int i = 1; i <= n; i ++) {
cin >> a[i].aa >> a[i].bb >> a[i].m;
a[i].id = i;
a[i].len = a[i].aa + a[i].bb - a[i].m;
a[i].l = max(0, a[i].aa - a[i].m);
a[i].r = a[i].aa - max(0, a[i].m - a[i].bb);
}
sort(a + 1, a + 1 + n, cmp);
for (int l = 1; l <= n; l ++) {
int r = l;
while (r <= n && a[r].len == a[l].len) r ++;
for (int i = l; i < r; i ++) {
int j = i;
while (j < r && a[j].l <= a[i].r ) p[a[j].id] = a[i].r, j ++;
i = j - 1;
cnt ++;
}
l = r - 1;
}
sort(a + 1, a + 1 + n, cmp2);
cout << cnt << endl;
for (int i = 1; i <= n; i ++)
cout << a[i].aa - p[a[i].id] << ' ' << a[i].m - (a[i].aa - p[a[i].id])<< endl;
}
return 0;
}
两篇博客讲解了如何在宴会准备中优化菜品分配,第一篇通过计算求解使∑a-∑b之差最小的操作方案,第二篇则是关于如何确保吃完后不同菜品组合最少。涉及算法包括贪心和区间合并思想。
1488

被折叠的 条评论
为什么被折叠?



