Codeforces Round #693 (Div. 3) E.Correct Placement【贪心 | 两点 | iota】

题目链接

题目描述:

给出n个人的图像,有高和宽,图像可以横着也可以竖着,对于两张图像 w i , h i w_i,h_i wi,hi w j , h j w_j, h_j wj,hj,如果i能在j前面,当且仅当满足下面两个条件之一:(1) w i < w j w_i<w_j wi<wj h i < h j h_i<h_j hi<hj;(2) h i < w j h_i<w_j hi<wj w i < h j w_i<h_j wi<hj,对于每张图像,问是否能找到一张图像在它前面,若能,输出下标,若不能输出-1。


题解:

错误想法:

一开始我认为找一个高和宽最小的图像m(当高和宽都更小时更新最小值),然后让每张图像x与m进行比较确定是否可以有一张图像在x前面,反例是,如果一个图像r,它的高很大而宽极小,它本来可以在另一张图像y(高更大,宽比r大)前面,但是因为r的高很大,导致m不能进行更新,而可能m的宽比y大,导致m不能在y前面,而实际上r可以在y的前面。

正解:

由题目描述的比较方式我们可知,高和宽其实不用区分,所以我们让小的为高,大的为宽,然后以其中一种标准来排序,例如按高由小到大到达排序,这样对于每一张图像,只需判断当前图像的宽是否大于一个维护值,这个维护值是比当前图像高度小的图像的最小的宽。对于高度的处理,我们只需要将同一高度的图像放在一起处理,方法是利用两点法确定高度相同的图像的下标范围,那么前面的图像的高度一定就小于当前这些图像的高度,也就只需要判断宽度就可以了,在处理完同等高度的图像后,还要利用这些图像去更新最小宽度,以供下次要处理的那些图像(高度更高)使用。

C++技巧

  1. iota,头文件是numeric,可以实现一个递增的赋值,例如:iota(p.begin(), p.end(), 0);,可以将p的每个元素赋值为0,1,2,…
  2. 如果要保存排序后每一个元素原来的位置,可以使用结构体,但是也可以使用一个数组p,来保存元素排序后的位置,即数组p的下标 i 为元素原来的位置,p[i]为排序后的位置。

AC Codes:

#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
//#include <unordered_set>
//#include <unordered_map>
#include <deque>
#include <list>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <numeric>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
//#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
//cout << fixed << setprecision(2);
//cout << setw(2);
const int N = 2e5 + 6, M = 1e9 + 7, INF = 0x3f3f3f3f;



int main() {
    //freopen("/Users/xumingfei/Desktop/ACM/test.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<int> h(n), w(n);
        for (int i = 0; i < n; i++) {
            cin >> h[i] >> w[i];
            if (h[i] > w[i]) {
                swap(h[i], w[i]);
            }
        }
        vector<int> p(n);
        iota(p.begin(), p.end(), 0);
        sort(p.begin(), p.end(), [&](int i, int j) {
            return h[i] < h[j];
        });
        vector<int> ans(n, -1);
        int u = -1;
        for (int i = 0; i < n; ) {
            int j = i;
            while (j < n && h[p[i]] == h[p[j]]) {
                j++;
            }
            for (int k = i; k < j; k++) {
                if (u != -1 && w[u] < w[p[k]]) {
                    ans[p[k]] = u;
                }
            }
            for (int k = i; k < j; k++) {
                if (u == -1 || w[u] > w[p[k]]) {
                    u = p[k];
                }
            }
            i = j;
        }
        for (int i = 0; i < n; i++) {
            cout << ans[i] + (ans[i] >= 0) << " \n"[i == n - 1];
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值