惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

I
InfoQ
S
SegmentFault 最新的问题
T
Tailwind CSS Blog
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
爱范儿
爱范儿
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
量子位
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
J
Java Code Geeks
The Cloudflare Blog
小众软件
小众软件
云风的 BLOG
云风的 BLOG
WordPress大学
WordPress大学
P
Proofpoint News Feed
Stack Overflow Blog
Stack Overflow Blog
aimingoo的专栏
aimingoo的专栏
Y
Y Combinator Blog
B
Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
C
Check Point Blog

博客园 - 糖豆爸爸

【树上DP前导知识汇总】 快速幂、龟速乘总结 反向建图+拓扑排序 卡特兰数专题(Catalan) AcWing 126. 最大的和 AcWing 431. 守望者的逃离 AcWing 414. 数字游戏 AcWing 468. 魔法阵 AcWing 463. 求和 洛谷 P1632 点的移动 洛谷 P1889 士兵站队 洛谷 P1862 输油管道问题 CF444C DZY Loves Colors P2253 好一个一中腰鼓! SCOI2010 P2572 序列操作 P4344 SHOI2015 脑洞治疗仪 T125847 【模板】动态开点线段树 P3373 【模板】线段树 2 Physical Education Lessons
P1056 NOIP2008 普及组 排座椅
糖豆爸爸 · 2023-09-19 · via 博客园 - 糖豆爸爸

\(P1056\) [\(NOIP2008\) 普及组] 排座椅 题解

先想一下算法:因为题目里出现了 最优解 , 最好的方案 关键字,所以一定会用 贪心。然后从题目给的样例解释可以看到:如果相邻的两行有许多组说话的同学,那么在这两行中间加一条过道是非常划算的;同理,列也是如此。

恍然大悟,只要找出划分哪些相邻的两行和相邻的两列可以隔开的同学最多,此题可解。

接下来是找规律:

  • 我们先定义两个数组\(x,y\):

    • \(x[1]\)表示如果在第一列与第二列中间划分过道能够分开几组说话的同学,同理,\(x[2]\)则是第二列与第三列... 直到\(x[n-1]\)
    • \(y[1]\)表示第一行与第二行,\(y[2]\)表示第二行与第三行... 直到\(y[m-1]\)
  • 题目输入两个同学的坐标,如果横坐标相同,即这两个同学在一行,那么设两个同学纵坐标分别为\(a,b\) 如果\(a<b\) 那么\(x[a]++\) 否则\(x[b]++\)(这里一定要特判一下\(a\)\(b\)的大小) 同理 \(y\) 数组也如此操作即可。

  • 最后\(x,y\)数组分别扫一遍,然后桶排一下即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;

int m, n, k, l, d; // m行n列,k条横向的通道,l条纵向的通道,d对同学交头接耳
int y[N], x[N];    // 横纵坐标数组
int b1[N], b2[N];  // 桶排要用的数组

int main() {
    cin >> m >> n >> k >> l >> d;

    while (d--) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        if (x1 == x2)         // 横坐标相等,则记录到个数数组中
            y[min(y1, y2)]++; // 表示隔开这两排的价值
        else
            x[min(x1, x2)]++; // 记得取min,即过道与前一个坐标保持一致
    }

    for (int i = 1; i <= k; i++) { // 循环k次
        int mx = 0;                // 临时最大值
        int p;                     // 临时最大值位置
        for (int j = 1; j <= m; j++) {
            if (x[j] > mx) {
                mx = x[j];
                p = j;
            }
        }
        x[p] = 0; // 求出max之后一定要记得清零!!否则无论排多少次都是一个答案
        b1[p]++;  // p这行是需要加入过道的
    }

    for (int i = 1; i <= l; i++) {
        int mx = 0;
        int p;
        for (int j = 1; j <= n; j++) {
            if (y[j] > mx) {
                mx = y[j];
                p = j;
            }
        }
        y[p] = 0; // 同上
        b2[p]++;  // p这列是需要加入过道的
    }

    // 输出答案
    for (int i = 1; i <= m; i++)
        if (b1[i]) printf("%d ", i); // 表示需要隔开这行

    puts("");

    for (int i = 1; i <= n; i++)
        if (b2[i]) printf("%d ", i);
    return 0;
}