编程之美第二题:长方形

时间限制: 1000ms 内存限制: 256MB

 

描述

在 N 条水平线与 M 条竖直线构成的网格中,放 K 枚石子,每个石子都只能放在网格的交叉点上。问在最优的摆放方式下,最多能找到多少四边平行于坐标轴的长方形,它的四个角上都恰好放着一枚石子。

输入

输入文件包含多组测试数据。

第一行,给出一个整数T,为数据组数。接下来依次给出每组测试数据。

每组数据为三个用空格隔开的整数 N,M,K。

输出

对于每组测试数据,输出一行"Case #X: Y",其中X表示测试数据编号,Y表示最多能找到的符合条件的长方形数量。所有数据按读入顺序从1开始编号。

数据范围

1 ≤ T ≤ 100

0 ≤ K ≤ N * M

小数据:0 < N, M ≤ 30

大数据:0 < N, M ≤ 30000

#include<stdio.h>
#include<math.h>
int main()
{
    //freopen("1.txt", "r", stdin);
    //freopen("2.txt", "w", stdout);
    int t,n,m,k,n1,m1,left,c=1;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&m,&k);
        if(n>m){
            n=m+n;
            m=n-m;
            n=n-m;
        }//so cool!
        //printf("%d  %d",n,m);
        n1=n<sqrt(k)?n:sqrt(k);
        m1=m;
        while(n1*m1>k){
            m1--;
        }
        long long ans=0;
        long long res=0;
        while(n1>=2 && m1<=m){
            res+=n1*m1*(n1-1)*(m1-1)>>2;
            left=k-(m1*n1);
            if(m1<m)
                res+=m1*left*(left-1)>>1;
            else
                res+=n1*left*(left-1)>>1;
            ans=res>ans?res:ans;
            res=0;
            n1--;
            m1=k/n1;
        }
        printf("Case #%d: %lld\n",c,ans);
        c++;
    }
        return 0;
}

这题是看了别人的解法才明白的。

t是控制数据组数,n,m是输入的网格的长和宽,注意:长为n-1的长方形长有n条线构成。然后是k,k是在长和宽两条线相交的交点处放的石头的个数,因此k的值是和长宽的线条数有关的。比如:k=6时,k=2*3, k=8时,k=2*4,或k=2*3+2;因此通过(int)sqrt(k),我们可以得到这么多k个石头所形成的长方形中短一条边的线条数n1(当然不是最短,最短为2,我们后面while中当短的边>=2时依次让n1--,判断其他长方形。。)m1是对应n1的长的一边的线条数,它们与k的关系是m1*n1+left=k,left是形成一个长方形后多下点。可见k>=m1*n1. 

通过上面我们得到了能够形成的最大的长方形的m1和n1(两边的线条数),它所包含的长方形个数为:在长的线条中选择两条和在宽的线条中选择两条!

C(2,n1)*C(2,m1)。然后就考虑剩下的点,left=k-n1*m1,我们首先考虑将剩下的点加入到较长的边(如果m1<m),因为这样形成的长方形肯定多!,如果加不下,那也没办法只能加到短的边,但是不论加到哪个边上,所最多能多形成的长方形个数为:在多的点里面选择两个(形成一条线),在所加的边的线里面选择一条线,C(1,m1)*C(2,left)(加到长的边m1),n1也一样。

最后递减n1,求出m1,如果n1>=2 && m1<= m,说明还能形成其他长方形和多余的点left,计算,取所能形成的最大的值。

 

转载于:https://www.cnblogs.com/byking/archive/2013/04/07/second.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值