One-Dimensional Battle Ships CodeForces - 567D (二分做法+解释)

One-Dimensional Battle Ships

CodeForces - 567D

Alice and Bob love playing one-dimensional battle ships. They play on the field in the form of a line consisting of n square cells (that is, on a 1 × n table).

At the beginning of the game Alice puts k ships on the field without telling their positions to Bob. Each ship looks as a 1 × a rectangle (that is, it occupies a sequence of a consecutive squares of the field). The ships cannot intersect and even touch each other.

After that Bob makes a sequence of "shots". He names cells of the field and Alice either says that the cell is empty ("miss"), or that the cell belongs to some ship ("hit").

But here's the problem! Alice like to cheat. May be that is why she responds to each Bob's move with a "miss".

Help Bob catch Alice cheating — find Bob's first move, such that after it you can be sure that Alice cheated.

Input

The first line of the input contains three integers: n, k and a (1 ≤ n, k, a ≤ 2·105) — the size of the field, the number of the ships and the size of each ship. It is guaranteed that the n, k and a are such that you can put k ships of size a on the field, so that no two ships intersect or touch each other.

The second line contains integer m (1 ≤ m ≤ n) — the number of Bob's moves.

The third line contains m distinct integers x1, x2, ..., xm, where xi is the number of the cell where Bob made the i-th shot. The cells are numbered from left to right from 1 to n.

Output

Print a single integer — the number of such Bob's first move, after which you can be sure that Alice lied. Bob's moves are numbered from 1 to m in the order the were made. If the sought move doesn't exist, then print "-1".

Example
Input
11 3 3
5
4 8 6 1 11
Output
3
Input
5 1 3
2
1 5
Output
-1
Input
5 1 3
1
3
Output
1
题目意思:给出 1 * n 的 field,编号从左至右依次为 1,2,...,n。问射 m 枪之后(第 i 次射中编号 xi,则 xi 这一点是不能放置船只的!),能不能将 k 只 1 * a 的小船放到这些没有经过被射中编号的 field 中 。由于Alice 每次 shoot 的时候都会说 miss 的,即没有打中,你需要判断第几次shoot 使得整个field 不能放置 k 只 小船,如果都能放,最终输出 - 1.


分析:根据题意我们知道放置每条船的时候每条船不能相接触(也就是不能相连,船的摆放不能是连续的),所以每条船之间相隔1的距离即可,那么对于长度为n的表,我们可以列出方程 ,设可以放置x条船,则有(a*x)+(x-1)*1 = n.  (解释:放置x条船中间就会有x-1个为1间隔总长度为n)解得  x = (n+1)/(a+1) (注意这里的n代表实际我要放船的长度,+1实际是虚的我们假设多出1)这里对于后面解题是关键

举个例子a=1 ,总表长n = 7

有                            1     2     3     4     5     6     7     8(虚的我们假设多1)

                               船           船          船           船       

所以实际一条船占2个位置,这样我们正好算出这个长度下可以放8/2=4条船

那么利用二分,边界是l=1,r=m二分出轰炸的次数times,根据前times次轰炸的位置(已知),我们可以把原本总的长度为n的表分成times+1个部分,对着times+1个部分,分别计算出每个部分可以放下船的个数,求个总和,然后和k(实际放的个数)作比较(这就是代码中check函数的作用)

如果总和恰好等于k或者大于k说明,爱丽丝完全可以在鲍勃轰炸这times个位置的情况下放置大于等于条船,因此可以骗过鲍勃,也就是鲍勃需要检查更多次,此时舍去左区间即l = mid+1,反之,如果总数小于了k,说明在鲍勃轰炸times个位置的情况下,爱丽丝最多最多放的船小于k条,因为实际放了k条说明放的多出的那几条肯定能被炸到,说明此时的tiems可以说明爱丽丝骗人了,但还要继续二分找最小,所以舍去右区间r=mid-1.

整体思路是这样

具体代码细节的分析将在代码中解释:

code:


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 200100
#define INF 0x3f3f3f3f
using namespace std;
int n,k,a,m;
int x[MAXN],x2[MAXN];
int check(int times){//解这道题的关键就在于这个check函数
    int i;
    for(i = 1; i <= times; i++){//把前times次的轰炸位置暂存下来
        x2[i] = x[i];
    }
    sort(x2+1,x2+times+1);//排好序,这样从头往后遍历被分成的部分
    int l = 1,tot = 0;
    for(i = 1; i <= times; i++){
        //这里需要花时间解释一下,因为这里我也好久才明白。根据前面解释在长度为n的长度内
        //可以放置的船的条数是(n+1)/(a+1),n是实际的放置长度而+1是虚的只是我们故意多出1,这样整除出结果
        //但是问题是我将整个长度分成了连续的times+1个部分,如果每部分都加上虚的1,就把整体表变长了
        //而我们还得有间隔,那么只好让每部分的末尾舍去1,这样对于中间的长度为nn的每部分,我们实际的放置
        //长度是nn-1,而我们是从下标1开始记录,所以x2[i]-L恰好是L~x2[i]这段长度-1,然后根据公式实际长度是
        //nn-1 = x2[i]-L,所以放置个数为x2[i]-L+1/(a+1),也就是下面代码的这样写的原因
        int len = (x2[i]-l)+1;
        tot += len/(a+1);
        l = x2[i]+1;
    }
    //上面说的是中间部分,而对于最后一部分,因为最后一条船可以放在表尾边上,不需要空出一个1
    //或者说我们可以假设后面多一个虚的1,这样的话,实际放置长度就应该是L~n的长度n-L+1,然后根据公式
    //再加1除(a+1)得到最后一部分放置船的个数
    tot += ((n-l+1)+1)/(a+1);
    //最后求和比较
    if(tot >= k)
        return 1;
    return 0;
}
int main(){
    scanf("%d%d%d%d",&n,&k,&a,&m);
    int i;
    for(i = 1; i <= m; i++)
        scanf("%d",&x[i]);
    int l = 1,r = m,ans = INF;//边界
    while(l<=r){//二分
        int m=(l+r)/2;
        if(check(m))//前面整体思路中已经解释,不在赘述
            l = m+1;
        else{
            r = m-1;
            ans = min(ans,m);
        }
    }
    if(ans == INF)
        ans = -1;
    printf("%d\n",ans);
    return 0;
}



展开阅读全文

没有更多推荐了,返回首页