在做七夕祭这道题目的时候,感觉很难以下手,看了题解,将七夕祭模型使用简单的模型递推之后变得简单了,也就是从均分纸牌到七夕祭的过程
均分纸牌 AcWing1536
#include <iostream>
using namespace std;
const int N = 105;
int n, avg, ans, a[N];
int main()
{
cin >> n;
for(int i = 0; i < n; i ++ )
{
cin >> a[i];
avg += a[i];
}
avg = avg / n;
for(int i = 0; i < n; i ++ ) a[i] -= avg;
for(int i = 0; i < n; i ++)
{
if(a[i] == 0) continue;
else
{
if(i == 0) a[i+1] += a[i];
else if(i == n-1) a[i-1] += a[i];
else a[i+1] += a[i];
ans ++;
}
}
cout << ans << endl;
return 0;
}
题目限制了首个和某尾只能朝着中间传递,所以计算每个纸牌和平均值的差,传递方向因为限制所以就是向右边传递直到最后一个,将每张纸牌的差值向右边传递,如果当前节点已经为0,就是不需要传递的,如果不为0就计入传递次数,也就是ans++。直到传递完成即可。
糖果传递 AcWing122
设小朋友每个人的糖果个数为 A i A_i Ai,那么就是 A 1 , A 2 , . . . , A n A_1, A_2, ..., A_n A1,A2,...,An。那么最后均分完糖果,每个人的糖果个数为 a v g = A 1 + A 2 + . . . + A n n avg = \frac{A_1+A_2+...+A_n}{n} avg=nA1+A2+...+An
若想要达到这个结果那么假设 A 2 A_2 A2 向 A 1 A_1 A1传递的糖果个数为 X 2 X_2 X2,注意,这里的 X 2 X_2 X2可能存在负数情况,也就是说同时考虑到两个方向(即正数 A 2 A_2 A2朝 A 1 A_1 A1,负数反之),这里的 X 1 X_1 X1就是 A 1 A_1 A1向 A n A_n An传递的糖果数。
那么题目要的代价 a n s = ∣ X 1 ∣ + ∣ X 2 ∣ + . . . + ∣ X n ∣ ans = |X_1|+|X_2|+...+|X_n| ans=∣X1∣+∣X2∣+...+∣Xn∣,我们的目标转化为求这个代价的最小值。
此时我们进行转换 我们发现 A 1 − X 1 + X 2 = a v g A_1 - X_1 + X_2 = avg A1−X1+X2=avg,这个等式意思是, A 1 给 A n X 1 个 糖 果 , 拿 到 A 2 给 A 1 的 X 2 个 糖 果 , 最 后 得 到 的 值 就 是 a v g A_1给A_n X_1个糖果,拿到A_2给A_1的X_2个糖果,最后得到的值就是avg A1给AnX1个糖果,拿到A2给A1的X2个糖果,最后得到的值就是avg
此 时 X 2 = X 1 − A 1 + a v g 此时X_2 = X_1 - A_1 + avg 此时X2=X1−A1+avg,我们定义 C 1 = A 1 − a v g C_1 = A_1 - avg C1=A1−avg,那么等式变为 X 2 = X 1 − C 1 X_2 = X_1 - C_1 X2=X1−C1
那么递推其余的
A
2
−
X
2
+
x
3
=
a
v
g
A_2 - X_2 + x_3 = avg
A2−X2+x3=avg
X
3
=
X
2
−
A
2
+
a
v
g
=
X
—
—
1
−
C
1
−
A
2
+
a
v
g
X_3 = X_2 - A_2 + avg = X——1 - C_1 - A_2 + avg
X3=X2−A2+avg=X——1−C1−A2+avg
设
C
2
=
C
1
+
A
2
−
a
v
g
=
A
1
+
A
2
−
2
a
v
g
设C_2= C_1 + A_2 - avg = A_1 + A_2 - 2avg
设C2=C1+A2−avg=A1+A2−2avg
X
3
=
X
1
−
C
2
由
此
得
出
递
推
式
X
n
=
X
1
−
C
n
−
1
此
处
注
意
一
点
(
因
为
是
个
环
路
)
A
n
−
X
n
+
A
1
=
a
v
g
X_3 = X_1 - C_2由此得出递推式 X_n = X_1 - C_{n-1}此处注意一点(因为是个环路) A_n - X_n + A_1 = avg
X3=X1−C2由此得出递推式Xn=X1−Cn−1此处注意一点(因为是个环路)An−Xn+A1=avg
A
1
=
a
v
g
+
X
n
−
A
n
=
a
v
g
−
A
n
+
X
1
−
C
n
−
1
A_1 = avg + X_n - A_n = avg - A_n + X_1 - C_{n-1}
A1=avg+Xn−An=avg−An+X1−Cn−1
通过得到的递推式,我们得到代价 a n s = ∣ X 1 − C n ∣ + ∣ X 1 − C 1 ∣ + . . . + ∣ X 1 − C n − 1 ∣ ans = |X_1-C_{n}| + |X_1-C_1| + ... + |X_1 - C_{n-1}| ans=∣X1−Cn∣+∣X1−C1∣+...+∣X1−Cn−1∣
很容易可以看出来问题被转化成了在数轴上找一点使得其到其余点距离最小,那么通过之前的货仓选址题目,直接选择中位数即可。
// AcWing 122
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
ll n, avg, ans, maxc, minc, a[N], c[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
avg += a[i];
}
avg /= n;
for (int i = 0; i <= n; i++) {
c[i] = c[i - 1] + a[i] - avg;
}
sort(c + 1, c + n + 1);
int x1 = c[(n + 1) >> 1];
for (int i = 1; i <= n; i++) {
ans += abs(x1 - c[i]);
}
cout << ans << endl;
return 0;
}
七夕祭 AcWing104
那么我们来到七夕祭这道题目,通过糖果传递这道题目,我们可以轻易的看出七夕祭只是一个从排和列两个角度进行糖果传递的题目。
讨论是否存在传递完存在平均数,判断行列是否能通过传递得到每行或每列相同的商铺数。每种情况直接使用糖果传递做法解决即可。
直接可写出代码如下
// AcWing 104
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
ll n, m, t, row[N], column[N], c[N];
int main() {
cin >> n >> m >> t;
for (int i = 0, x, y; i < t; i++) {
cin >> x >> y;
row[x] += 1;
column[y] += 1;
}
if (t % n != 0 && t % m != 0) {
cout << "impossible" << endl;
} else if (t % n == 0 && t % m != 0) {
cout << "row ";
int avg = t / n;
for (int i = 1; i <= n; i++) {
c[i] = c[i - 1] + row[i] - avg;
}
sort(c+1, c+n+1);
int mid = c[(n+1) >> 1];
ll ans = 0;
for (int i = 1; i <= n; i++) {
ans += abs(mid - c[i]);
}
cout << ans << endl;
} else if (t % n != 0 && t % m == 0) {
cout << "column ";
int avg = t / m;
for (int i = 1; i <= m; i++) {
c[i] = c[i - 1] + column[i] - avg;
}
sort(c+1, c+m+1);
int mid = c[(m+1) >> 1];
ll ans = 0;
for (int i = 1; i <= m; i++) {
ans += abs(mid - c[i]);
}
cout << ans << endl;
} else {
cout << "both ";
int avg = t / n;
for (int i = 1; i <= n; i++) {
c[i] = c[i - 1] + row[i] - avg;
}
sort(c+1, c+n+1);
int mid = c[(n+1) >> 1];
int ans = 0;
for (int i = 1; i <= n; i++) {
ans += abs(mid - c[i]);
}
avg = t / m;
for (int i = 1; i <= m; i++) {
c[i] = c[i - 1] + column[i] - avg;
}
sort(c+1, c+m+1);
mid = c[(m+1) >> 1];
for (int i = 1; i <= m; i++) {
ans += abs(mid - c[i]);
}
cout << ans << endl;
}
return 0;
}
优化后代码,提出一直用的代码部分
// AcWing 104
#include <algorithm>
#include <iostream>
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;
int n, m, t, row[N], column[N], c[N];
ll getAns(int a[N], int l) {
ll ans = 0;
int avg = t / l;
for (int i = 1; i <= l; i++) {
c[i] = c[i - 1] + a[i] - avg;
}
sort(c + 1, c + l + 1);
int mid = c[(l + 1) >> 1];
for (int i = 1; i <= l; i++) {
ans += abs(mid - c[i]);
}
return ans;
}
int main() {
scanf("%d%d%d", &n, &m, &t);
for (int i = 0, x, y; i < t; i++) {
scanf("%d%d", &x, &y);
row[x] += 1;
column[y] += 1;
}
if (t % n == 0 && t % m == 0)
printf("both %ld", getAns(row, n) + getAns(column, m));
else if (t % n == 0)
printf("row %ld", getAns(row, n));
else if (t % m == 0)
printf("column %ld", getAns(column, m));
else
printf("impossible");
return 0;
}