Example
input
3
4
7 2 2 7
8
2 8 1 4 8 2 1 5
5
5 5 5 5 5
output
2 7 7 2
2 2 1 1
5 5 5 5
题目大意
从给出的n条边中选择4条边组成一个长方形,P表示周长,S表示面积,要求组成的长方形P2/S最小,题目保证给出的数据能组成一个长方形。
分析
设长方形的长为a,宽为b,则P=2(a+b),S=ab,所以P2/S=4(a+b)2/ab=4(a/b+b/a+2),要想使得P2/S最小,就应使a/b+b/a最小,当a==b时,即变为正方形时,P2/S最小。
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[10010];
int b[10010];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int k=0;
memset(a,0,sizeof(a));
int n;
scanf("%d",&n);
int x;
int flag=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
a[x]++;
if(a[x]==2) b[k++]=x;
if(a[x]>=4&&!flag)
{
printf("%d %d %d %d\n",x,x,x,x);
flag=1;
}
}
if(flag) continue;
double mx=0x3f3f3f3f;
int l,r;
sort(b,b+k);
for(int i=0;i<k-1;i++)
{
double ans=1.0*b[i+1]/b[i]+1.0*b[i]/b[i+1];
if(ans<mx)
{
mx=ans;
l=b[i];
r=b[i+1];
}
}
printf("%d %d %d %d\n",l,l,r,r);
}
return 0;
}
注意
1.if(a[x]==2) b[k++]=x;此处不能写作a[x]>=2,否则一条边会被重复加入b数组中,b数组存的是出现的所有边,相同长度的边只统计一次,如果一条边被多次存放,会造成数组越界,也会增加时间复杂度,导致RE或者TLE
2.将所有的边排序后,只需比较相邻长度的边的a/b+b/a的值,两条边长度差距越小,最终a/b+b/a的值也越小,即这个长方形越接近一个正方形,最优解一定在相邻长度的两条边中产生。
3.如果同一长度的边出现4次,那么就可以构成一个正方形,而正方形就是最优的情况,可以直接输出,题目要求输出一组最优解即可。
4.下面这段代码与以上代码解法一致,时间复杂度是O(n),由于题目数据量过大,最终提交结果是TLE。下面这段代码是先统计每种长度的边出现的次数,在选择出现次数大于等于2和大于等于4的边,由于题目上边长度的范围较大,而一组测试数据中出现的长度范围可能很小,在时间复杂度相同的情况下,下面这段代码的运行时间较长。上一段代码中是在统计每种长度的边出现的次数的同时,又判断出现次数到达2和4这两种情况,效率更高。
TLE代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[10010];
int b[10010];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int f=0;
int k=0;
memset(a,0,sizeof(a));
int n;
scanf("%d",&n);
int x;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
a[x]++;
if(x>f) f=x;
}
int flag=0;
for(int i=1;i<=f;i++)
{
if(a[i]>=2)
{
b[k++]=i;
}
if(a[i]>=4)
{
printf("%d %d %d %d\n",i,i,i,i);
flag=1;
break;
}
}
if(flag) continue;
double mx=0x3f3f3f3f;
int l,r;
for(int i=0;i<k-1;i++)
{
double ans=1.0*b[i+1]/b[i]+1.0*b[i]/b[i+1];
if(ans<mx)
{
mx=ans;
l=b[i];
r=b[i+1];
}
}
printf("%d %d %d %d\n",l,l,r,r);
}
return 0;
}