nyoj-687-Twinkle Twinkle Little Star【dp】【二分】【好题】

nyoj-687-Twinkle Twinkle Little Star

描述
Twinkle, twinkle, little star, how I wonder what you are.

Up above the world so high, like a diamond in the sky.

Twinkle, twinkle, little star, how I wonder what you are.

When the blazing sun is gone, when he nothing shines upon.

The you show your little light, Twinkle, twinkle, little star

Twinkle, twinkle, little star, How I wonder what you are.

Twinkle, twinkle, little star, how I wonder what you are.

—- < Twinkle, twinkle, little star. >

Well, this song may take us back to our childhood. When we were young, we often looked up at the stars. How amazing they were! But, unfortunately, as we are becoming older and older, what used to be interesting can not interest us now. So what we can do is to find something more interesting!

Here is one, maybe. Assume that all the stars are so far from us that we can treat them as points in a plane. You are given N stars in the plane, and a number K (0≤K≤N). What you need to do is to find the minimum square covering at least K stars, whose edges are all parallel to the axis. The stars which are on the edges of the square are also covered.

输入
The input will consist of multiple cases. Your program should process to the end of the input file.In the first line of one case, there are two integer N and K, 0 < N ≤ 1500, 0 ≤ K ≤ N.
The next N lines are the description of the stars, one star per line. The ith line consists of two integers Xi and Yi, |Xi| < 1000000, |Yi| < 1000000.
输出
The output will consist of one line for each case, in the format of “Case X: Y”, while X is the case number counting from 1, and Y is the edge length of the minimum square. X and Y are all integers.
样例输入
4 4
0 0
0 1
1 0
2 2
4 2
0 0
1 1
2 2
3 3
样例输出
Case 1: 2
Case 2: 1

题目链接:nyoj-687

参考博客:here

题目大意:有n个星星在一个平面上,现在用正方形去覆盖星星,问最少多长的正方形边长能覆盖至少k颗星星。(都为整数)

题目思路:dp + 二分。
①首先我们用p存储每个点
②用num_x数组存储所有点的横坐标,对应的用num_y数组存储所有点的纵坐标
③分别将num_x,num_y排序去重,离散化点,给每个值一个编号,分别记录在ret_x和ret_y里面

例如:假设存在5个点,分别为:[30,90], [100,36], [45,23], [67,129], [89,90]
构建一个表格,如图

nyoj-687-Twinkle Twinkle Little Star【dp】【二分】【好题】_第1张图片

④用vis数组记录每个格点上有几颗星星

⑤ dp处理,dp[i][j]记录从1,1到i,j存在几颗星星。

eg : dp[4][3] = 3 (红框显示)

dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + vis[i][j]; (如图即 1区 + 2区 - (1、2区公共部分) + i,j这个格点上有多少颗星星)
nyoj-687-Twinkle Twinkle Little Star【dp】【二分】【好题】_第2张图片

⑥二分正方形的边长,mid为边长。

先分别求出,以i为起点,横纵坐标最远能到达哪个点(边长在mid范围内)

然后遍历正方形的左上角,判断是否满足条件

以下是代码:

#include <bits/stdc++.h>
#define mst(a) memset(a,0,sizeof (a))
#define FOR(i,n) for (int i = 0; i < n; i++)
#define INF 1e9
#define eps 1e-10
using namespace std;

typedef long long ll;
struct node
{
    int x,y;
}p[1505];
int num_x[1505],num_y[1505];
map <int,int> ret_x,ret_y;
int vis[1505][1505];
int dp[1505][1505];
int dx[1505],dy[1505];
int main(){
    int n,k;
    int loy = 0;
    while(cin >> n >> k)
    {
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        for (int i = 0; i < n; i++)
        {
            cin >> p[i].x >> p[i].y;
            num_x[i] = p[i].x;
            num_y[i] = p[i].y;
        }
        sort(num_x,num_x + n);  //排序 
        sort(num_y,num_y + n);
        int n1 = unique(num_x, num_x + n) - num_x;  //去重 
        int n2 = unique(num_y, num_y + n) - num_y;
        int cnt_x = 0;
        for (int i = 0; i < n1; i++) ret_x[num_x[i]] = ++cnt_x; //哈希横坐标 
        int cnt_y = 0;
        for (int i = 0; i < n2; i++) ret_y[num_y[i]] = ++cnt_y; 
        for (int i = 0; i < n; i++)
        {
            vis[ret_x[p[i].x]][ret_y[p[i].y]]++;   //记录这个格点上有几个点 
        } 
        //dp处理从1,1到 i,j这个正方形包括多少个点。
        for (int i = 1; i <= cnt_x; i++)
        {
            for (int j = 1; j <= cnt_y; j++)
            {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + vis[i][j];
            }
        } 
        int l = 0, r = 2000005;
        while(l < r)  //二分正方形的边长 
        {
            int ok = 0;
            int mid = (l + r) / 2;
            for (int i = 1; i <= cnt_x; i++)  //mid为正方形最大边长,找到从i点为起始点,所能达到的最远点。 
            {
                for (int j = i; j <= cnt_x; j++)
                {
                    if (num_x[j - 1] - num_x[i - 1] <= mid) dx[i] = j;//两个点之间的长度在正方形边长mid以内
                    else break;  //如果这个点不满足,后面的点与i点的距离一定都更大so不满足 
                }
            }
            //同理找出y上面对应i所能达到最远的j
            for (int i = 1; i <= cnt_y; i++)
            {
                for (int j = i; j <= cnt_y; j++)
                {
                    if (num_y[j - 1] - num_y[i - 1] <= mid) dy[i] = j;
                    else break;
                }
            } 
            //枚举正方形的左上角位置 
            for (int i = 1; i <= cnt_x; i++)
            {
                for (int j = 1; j <= cnt_y; j++)
                {
                    int a = dx[i];
                    int b = dy[j];
                    int ans = dp[a][b] - dp[i - 1][b] - dp[a][j - 1] + dp[i - 1][j - 1];
                    if (ans >= k)
                    {
                        ok = 1;
                        break;
                    }
                }
                if (ok) break;
            }
            if (ok == 1) r = mid;
            else l = mid + 1;
        } 
        printf("Case %d: %d\n",++loy,l);  
    } 
    return 0;
}

你可能感兴趣的:(dp,二分,nyoj,687)