• 1222/2516. Kup


    题目描述

    Description

    首先你们得承认今天的题目很短很简洁。。。
    然后,你们还得承认接下来这个题目的描述更加简洁!!!
    Task:给出一个N*N(1≤N≤2000)的矩阵,还给出一个整数K。要你在给定的矩阵中
    求一个子矩阵,这个子矩阵中所有数的和的范围要在[k,2*k] 这个区间。
    如果有多个这样的子矩阵,请随便输出一个。

    Input

    第一行包含两个整数K 和N(1≤K≤10^8,1≤N≤2000)。其意义如题目描述!
    接下来有N 行,每行有N 个数,表示题目给出的矩阵。矩阵中的数都是非负数,而且
    不大于maxlongint。

    Output

    输出文件仅包含一行,四个整数,分别是你找出来的矩阵的左上角坐标和右下角坐标。
    如果不存在这样的子矩阵,请输出0 0 0 0。

    Sample Input

    Sample Input1:
    4 3
    1 1 1
    1 9 1
    1 1 1
    
    
    Sample Input2:
    8 4
    1 2 1 3
    25 1 2 1
    4 20 3 3
    3 30 12 2
    
    
    Sample Input3:
    8 4
    12 2 1 3
    25 1 2 1
    4 20 3 3
    3 30 12 2
    

    Sample Output

    Sample Output1:
    0 0 0 0
    
    
    Sample Output2:
    1 2 2 4
    
    
    Sample Output3:
    1 1 1 1
    

    Data Constraint

    Hint

    数据约定:
    对于30%的数据,1≤N≤5
    对于60%的数据,1≤N≤60
    对于100%的数据1≤N≤2000

    题解

    一道神题

    首先>2k的数肯定不能选,所以先找一个不包含>2k的数的最大矩阵

    用栈可以O(n^2)求出

    然后讨论一下

    ①sum<k

    无解

    ②k<=sum<=2k

    当前矩阵即为解

    ③sum>2k

    设当前矩阵中第一行的和为Sum

    再讨论一下

    1、Sum<k

    那么用sum-Sum,不会超过k的边界,所以减掉后继续

    2、k<=Sum<=2k

    当前行即为解

    3、Sum>2k

    由于保证了矩阵中没有>2k的数,所以依次把该行中的第一个数删掉

    再再讨论一下

    设删掉的数大小为a

    A、a<k

    那么Sum-a不会超过边界,减掉后继续

    B、k<=a<=2k

    a即为解

    C、a>2k

    不存在

    code

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    using namespace std;
    
    int a[2001][2001];
    int f[2001][2001];
    long long sum[2001][2001];
    int d[2001][2];
    int K,K2,n,i,j,k,l,x1,y1,x2,y2,t;
    long long mx,Sum;
    bool bz;
    
    long long get(int x1,int y1,int x2,int y2)
    {
    	return sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
    }
    
    int main()
    {
    //	freopen("kup.in","r",stdin);
    //	freopen("kup.out","w",stdout);
    	
    	scanf("%d%d",&K,&n);K2=K*2;
    	fo(j,1,n) f[0][j]=1;
    	fo(i,1,n)
    	{
    		fo(j,1,n)
    		{
    			scanf("%d",&a[i][j]);
    			
    			if (a[i][j]<=K2)
    			f[i][j]=f[i-1][j];
    			else
    			f[i][j]=i+1;
    			
    			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
    		}
    	}
    	
    	fo(i,1,n)
    	{
    		t=0;
    		fo(j,1,n)
    		{
    			bz=0;
    			while (t && d[t][0]<f[i][j])
    			{
    				Sum=get(d[t][0],d[t][1],i,j-1);
    				if (Sum>mx)
    				{
    					mx=Sum;
    					x1=d[t][0];y1=d[t][1];
    					x2=i;y2=j-1;
    				}
    				
    				--t;
    				bz=1;
    			}
    			
    			if (f[i][j]<=i && (!t || f[i][j]<d[t][0]))
    			{
    				++t;
    				d[t][0]=f[i][j];
    				if (!bz)
    				d[t][1]=j;
    			}
    		}
    		while (t)
    		{
    			Sum=get(d[t][0],d[t][1],i,n);
    			if (Sum>mx)
    			{
    				mx=Sum;
    				x1=d[t][0];y1=d[t][1];
    				x2=i;y2=n;
    			}
    			
    			--t;
    		}
    	}
    	
    	if (mx<K)
    	{
    		printf("0 0 0 0
    ");
    		return 0;
    	}
    	while (mx>K2)
    	{
    		Sum=get(x1,y1,x1,y2);
    		
    		if (Sum>=K)
    		{
    			x2=x1;
    			mx=Sum;
    			
    			while (mx>K2)
    			{
    				if (a[x1][y1]<K)
    				mx-=a[x1][y1++];
    				else
    				{
    					mx=a[x1][y1];
    					y2=y1;
    				}
    			}
    		}
    		else
    		mx-=Sum,++x1;
    	}
    	
    	printf("%d %d %d %d
    ",x1,y1,x2,y2);
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    
  • 相关阅读:
    接收一次性广播,开启服务永久监听
    iOS开发之主题皮肤
    Android软件版本更新
    android服务Service(上)- IntentService
    Android之条码扫描二维码扫描
    Android之Service与IntentService的比较
    强烈推荐visual c++ 2012入门经典适合初学者入门
    转载文章:Windows Azure 七月份更新:SQL 数据库、流量管理器、自动伸缩、虚拟机
    CSV 客座文章系列:KGroup 通过 Windows Azure 将 Qoob 内容管理发布到云中
    Windows Azure 网站:应用程序字符串和连接字符串的工作原理
  • 原文地址:https://www.cnblogs.com/gmh77/p/11828845.html
Copyright © 2020-2023  润新知