AcWing 105.七夕祭

Posted qq136155330

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AcWing 105.七夕祭相关的知识,希望对你有一定的参考价值。

题目描述

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

于是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

思路在代码里面

C++ 代码

#include <bits/stdc++.h>
using namespace std;
/**
 首先先考虑不是环的情况下
 那么就是普通的均匀分牌问题
 那么最少次数就是互相给牌
 if(hang[i] < t / n)
 hang[i + 1] -= (t / n - hang[i]), hang[i] += (t / n - hang[i]);
 else
 hang[i + 1] += (hang[i] - t / n), hang[i] -= (hang[i] - t / n);
 (假设hang数组内的值不发生变化,注意不要从0开始,数据是从1开始的,哭了)
 第一种情况下的状态
 hang[1] = hang[1] - (t / n - hang[0]) = hang[1] + hang[0] - t / n;
 第二种情况下的状态
 hang[1] = hang[1] + (hang[0] - t / n) = hang[1] + hang[0] - t / n; 
 
 第一次的移动次数为abs(t / n - hang[0]);
 第二次的移动次数为abs(t / n - hang[1] - hang[0] + t / n);
                   abs(2 * t / n - (hang[1] + hang[0]));
 所以可得第i次移动的次数为abs(i * t / n - sum(hang[i - 1]));
        那么可以拆分成abs((t / n - hang[0]) + (t / n - hang[1]) + ...... + (t / n - hang[i]));
        预先处理将数组变成 t / n - hang[i]
        那么求一个前缀和就可以得到每次移动的步数

 如果是环形的话,存在相邻的交换给的话
 肯定不是最优解,最优解肯定只存在于不互相给的情况下
 因为比如a b c d e 是个环 a -> c b -> e,那么实际上
 这肯定不是最优解
 因为可以通过b -> c , a -> e来构造一个更优秀的答案
 所以最优解只存在于不互相给的情况下
 那么就选择一个不交换的点,将其转换成普通的均匀分牌问题
 那么就重新选择出了一个非环的序列
 那么如何选择这个不交换的点
 假设该点为k
 取k为原点
 那么k就变成了基点,那么我们先求出了全部的sum[i]的值
 k ~ n
 sum[k + 1] - sum[k]
 sum[k + 2] - sum[k + 1]
 ....
 过了n之后,因为过了n后,sum[0] + sum[n] - sum[k]
 构成了新的序列的前缀和
 sum[n] + sum[0] - sum[k]
 sum[n] + sum[k] - sum[k]
 
 那么我们要寻找一个合适的k值
 因为sum[n] + sum[0] - sum[k] or sum[k + 1] - sum[k]
 就是一维上的一个求距离的公式
 那么结论就是 取sum[k]的值为sum[1 ~ n]中的中位数
 **/
typedef long long ll;
const int MAXN = 100000 + 5;
int hang[MAXN];
int lie[MAXN];
ll hang_sum[MAXN];
ll lie_sum[MAXN];
struct NODE
{
    ll num;
    ll sum;
};
bool cmp(const NODE &a, const NODE &b)
{
    return a.sum < b.sum;
}
int n, m, t;
void Init()
{
    memset(hang, 0, sizeof(hang));
    memset(lie, 0, sizeof(lie));
    memset(hang_sum, 0, sizeof(hang_sum));
    memset(lie_sum, 0, sizeof(lie_sum));
}
void ycl()
{
    for(int i = 1; i <= n; i ++)
    {
        hang[i] = hang[i] - t / n;
    }
    for(int i = 1; i <= m; i ++)
    {
        lie[i] = lie[i] - t / m;
    }
    hang_sum[0] = hang[0];
    for(int i = 1; i <= n; i ++)
    {
        hang_sum[i] = hang_sum[i - 1] + hang[i];
    }
    lie_sum[0] = lie[0];
    for(int i = 1; i <= m; i ++)
    {
        lie_sum[i] = lie_sum[i - 1] + lie[i];
    }
}
pair<ll, ll> hang_acfinds()
{
    vector<NODE>vec;
    vec.clear();
    for(int i = 1; i <= n; i ++)
        {
            NODE temp;
            temp.num = i;
            temp.sum = hang_sum[i];
            vec.push_back(temp);
        }
    sort(vec.begin(), vec.end(), cmp);
    return {vec[vec.size() / 2].num, vec[vec.size() / 2].sum};
}
pair<ll, ll> lie_acfinds()
{
    vector<NODE>vec;
    vec.clear();
    for(int i = 1; i <= m; i ++)
        {
            NODE temp;
            temp.num = i;
            temp.sum = lie_sum[i];
            vec.push_back(temp);
        }
    sort(vec.begin(), vec.end(), cmp);
    return {vec[vec.size() / 2].num, vec[vec.size() / 2].sum};
}
ll result_hang(pair<ll, ll> num)
{
    ll re = 0;
    for(int i = 1; i <= num.first; i ++)
        re += abs(hang_sum[i] + hang_sum[n] - num.second);
    for(int i = num.first + 1; i <= n; i ++)
        re += abs(hang_sum[i] - num.second);
    return re;
}
ll result_lie(pair<ll, ll> num)
{
    ll re = 0;
    for(int i = 1; i <= num.first; i ++)
        re += abs(lie_sum[i] + lie_sum[m] - num.second);
    for(int i = num.first + 1; i <= m; i ++)
        re += abs(lie_sum[i] - num.second);
    return re;
}
int main()
{
    while(~scanf("%d%d%d", &n, &m, &t))
    {
        Init();
        for(int i = 0; i < t; i ++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            hang[a] ++;
            lie[b] ++;
        }
        bool flag1, flag2;
        flag1 = flag2 = false;
        ycl();
        ll re1, re2;
        if(t % n == 0)///在行的情况下,t个能被整除,才存在解
        {
            flag1 = true;
            pair<ll, ll>num;
            num = hang_acfinds();
            re1 = result_hang(num);
        }
        if(t % m == 0)
        {
            flag2 = true;
            pair<ll, ll>num;
            num = lie_acfinds();
            re2 = result_lie(num);
        }
        if(flag1 && flag2)
        {
            printf("both %lld
",re1 + re2);
        }
        else if(flag1)
        {
            printf("row %lld
", re1);
        }
        else if(flag2)
        {
            printf("column %lld
", re2);
        }
        else
        {
            printf("impossible
");
        }
    }
    return 0;
}

以上是关于AcWing 105.七夕祭的主要内容,如果未能解决你的问题,请参考以下文章

ACM - 贪心(经典母题+POJ一些练习题)

ACM - 贪心(经典母题+POJ一些练习题)

基本算法- 七夕祭

:七夕祭 (货仓选址+均分纸牌)

[bzoj3032][TYVJ P1924]七夕祭(环形均分纸牌,货仓选址)

bzoj3032 七夕祭题解