头像

Cyan

四川成都

深度强化学习炼丹师

AcWing-105-七夕祭

AcWing-105-七夕祭

2021-02-10 · 83次阅读 · 原创 · 数据结构与算法

问题

七夕节因牛郎织女的传说而被扣上了「情人节」的帽子。

于是TYVJ今年举办了一次线下七夕祭。

Vani同学今年成功邀请到了cl同学陪他来共度七夕,于是他们决定去TYVJ七夕祭游玩。

TYVJ七夕祭和11区的夏祭的形式很像。

矩形的祭典会场由N排M列共计N×M个摊点组成。

虽然摊点种类繁多,不过cl只对其中的一部分摊点感兴趣,比如章鱼烧、苹果糖、棉花糖、射的屋……什么的。

Vani预先联系了七夕祭的负责人zhq,希望能够通过恰当地布置会场,使得各行中cl感兴趣的摊点数一样多,并且各列中cl感兴趣的摊点数也一样多。

不过zhq告诉Vani,摊点已经随意布置完毕了,如果想满足cl的要求,唯一的调整方式就是交换两个相邻的摊点。

两个摊点相邻,当且仅当他们处在同一行或者同一列的相邻位置上。

由于zhq率领的TYVJ开发小组成功地扭曲了空间,每一行或每一列的第一个位置和最后一个位置也算作相邻。

现在Vani想知道他的两个要求最多能满足多少个。

在此前提下,至少需要交换多少次摊点。

输入格式

第一行包含三个整数N和M和T,T表示cl对多少个摊点感兴趣。

接下来T行,每行两个整数x, y,表示cl对处在第x行第y列的摊点感兴趣。

输出格式

首先输出一个字符串。

如果能满足Vani的全部两个要求,输出both;

如果通过调整只能使得各行中cl感兴趣的摊点数一样多,输出row;

如果只能使各列中cl感兴趣的摊点数一样多,输出column;

如果均不能满足,输出impossible。

如果输出的字符串不是impossible, 接下来输出最小交换次数,与字符串之间用一个空格隔开。

数据范围

1≤N,M≤100000,
0≤T≤min(N∗M,100000),
1≤x≤N,
1≤y≤M

输入样例:

2 3 4
1 3
2 1
2 2
2 3

输出样例:

row 1

思路

由题可知:对左右两个摊点的交换,只会改变两列的感兴趣的摊点数量;而对上下两个摊点的交换,也只会改变每两行的感兴趣摊点数量。则可以将本问题分为行满足和列满足两个子问题进行讨论:

  1. 通过最少次数的上下交换使每行中感兴趣的摊点数量一样
  2. 通过最少次数的左右交换使每列中感兴趣的摊点数量一样

而对于第一个问题,可以统计好每行的感兴趣摊点数量 row[i] ,则即为将 row[1]row[M] 都置为 T/M (每次可将 row[i] 中的一个摊点移动到 row[(i+1)%(M+1)+1] 或者 row[(i-1+M)%(M+1)+1])所需要的最少步数,类似于 纸牌均分 问题,可参照其思路,解出问题。

但需要注意,纸牌均分问题第一个元素和最后一个元素不为连通,而本题为连通的,则肯定能找到一个点 k,使得环状数组rowk后断开,断开的刚好能够符合纸牌均分问题的不连通性,则关键是找到 k 的坐标。其断开后的前缀和如下:

索引 新的数组(- avg(=T/M)) 新的前缀和数组(其中S[M]=0)
1 row[k+1] S[k+1] - S[k]
2 row[k+2] S[k+2] - S[k]
…… …… ……
M - k row[M] S[M] - S[k]
M - k +1 row[1] S[1] + S[M] - S[k]
…… …… ……
M row[k] S[k] + S[M] - S[k]

注意:上表中S[M]=0Srow 减去T/M后的前缀和,则最终结果 S[M] = 0

则结果为:

ans=min1kM{i=1M(S[i]S[k])}ans = \min_{1\le k \le M}\{\sum_{i=1}^{M}(S[i]-S[k])\}

则看到上式,可以知道此公式对应 货仓选址 问题,即将 S 排序后 k 取中位数下标时,可得到 ans


题解

#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N = 1e5 + 5; int n, m, t; int col[N], row[N]; int s[N]; int main() { cin >> n >> m >> t; bool st1 = false, st2 = false; long long res1, res2; int x, y; int cnt = 0; for (int i = 0; i < t; ++i) { scanf("%d%d", &x, &y); row[x]++; col[y]++; cnt++; } if (cnt % n == 0) { //总数可否被行数整除 st1 = true; int avg = cnt / n; for (int i = 1; i <= n; ++i) s[i] = s[i - 1] + row[i] - avg; sort(s + 1, s + n + 1); int mid = s[n / 2 + 1]; res1 = 0; for (int j = 1; j <= n; ++j) res1 += abs(s[j] - mid); } if (cnt % m == 0) { //总数可否被列数整除 st2 = true; int avg = cnt / m; for (int i = 1; i <= m; ++i) s[i] = s[i - 1] + col[i] - avg; sort(s + 1, s + m + 1); int mid = s[m / 2 + 1]; res2 = 0; for (int j = 1; j <= m; ++j) res2 += abs(s[j] - mid); } if (st1 && st2)cout << "both " << res1 + res2 << endl; else if (st1) cout << "row " << res1 << endl; else if (st2) cout << "column " << res2 << endl; else cout << "impossible" << endl; return 0; }

标题: AcWing-105-七夕祭
链接: https://www.fightingok.cn/detail/47
更新: 2022-09-18 22:34:14
版权: 本文采用 CC BY-NC-SA 3.0 CN 协议进行许可