1. 程式人生 > >51nod 1521 一維戰艦+set+二分

51nod 1521 一維戰艦+set+二分

題目連結:
一行有n個方格的紙上,愛麗絲放k個戰艦在這個表格中,並不把具體位置告訴鮑博。每一隻戰艦的形狀是 1×a 的長方形(也就是說,戰艦會佔據a個連續的方格)。這些戰艦不能相互重疊,也不能相接觸。然後鮑博會做一系列的點名。當他點到某個格子的時候,愛麗絲會告訴他那個格子是否被某隻戰艦佔據。如果是,就說hit,否則就說miss。但是這兒有一個問題!愛麗絲喜歡撒謊。他每次都會告訴鮑博miss。請你幫助鮑博證明愛麗絲撒謊了,請找出哪一步之後愛麗絲肯定撒謊了。如果不能證明輸出-1。
第一行有三個整數n,k和a。
第二行是一個整數m(1≤m≤n),表示鮑博的點名次數。
第三行有m個不同的整數x1,x2,…,xm,xi是鮑博第i次點名的格子編號。格子從左到右按照1到n編號。
在這裡插入圖片描述


思路:這道題當時想的是計算浪費的空間,當浪費的空間使得空間放不下戰艦為止。但是計算過的區間要標記,但是戰艦的位置不確定,所以區間無法標記。
然後也想了二分,計算能放下戰艦的最大數量,當最大數量<k就證明愛麗絲撒謊了。但是也感覺無法維護。。。。。

看了題解,就是二分。。。。
因為每個戰艦不能相接觸,我們可以把每次詢問x;相當在x空格插入一堵牆(開始插入0, n+1)。並把這個區間一分為二。ans=ans-之前這個區間能放下的最大數量+(左區間能放下的最大數量+右區間能放下的最大數量)

那麼第一種思路也能維護浪費的空間:ans=ans-之前這個區間浪費的空間+(左區間浪費的空間+右區間浪費的空間)

思考:當時還在想是不是考慮這個區間是不是最左最右區間,因為這樣可以可以放最左邊或最右邊。也考慮每次詢問是否相同。該用multiset還是set。
後來引入牆就發現,每次詢問都是同一種過程,而且詢問相同就是在之前有牆的地方放牆,什麼也沒有改變。

#include <bits/stdc++.h>
using namespace std;

set<int> s;
int main()
{
    int n, k, a, m, ans;
    scanf("%d%d%d",&n,&k,&a);
    ans=(n+1)/(a+1);
    s.insert(0);
    s.insert(n+1);

    scanf("%d",&m);
    int p=0;
    for(int i=1;i<=m;i++)
    {
        int f, l, r;
        scanf("%d",&f);
        l=*(--s.lower_bound(f));//區間左邊的牆
        r=*(s.upper_bound(f));//區間右邊的牆
        ans-=(r-l)/(a+1);//減去之前這個區間能放下的最大數量
        ans+=(f-l)/(a+1)+(r-f)/(a+1);//加上左區間能放下的最大數量+右區間能放下的最大數量
        s.insert(f);//插入牆

        if(ans<k&&p==0)
        p=1, cout<<i<<endl;
    }
    if(!p)
        cout<<-1<<endl;

    return 0;
}