1 条题解

  • 0
    @ 2025-8-24 21:23:21

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Cxs3
    这个家伙很懒,什么也不想留下

    搬运于2025-08-24 21:23:20,当前版本为作者最后更新于2019-09-10 12:28:32,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题目链接:https://www.luogu.org/problem/P1284

    题目分析

    做这道题首先要知道 已知三角形三边长求面积的公式:海伦公式

    接下来就是设状态。背包的题目难就难在这一步,设好状态后方程是很容易写的。

    f[a][b][c]f[a][b][c](布尔型)表示三边长为a,b,ca,b,c的三角形能否围成。但是,三角形的边长的最大值=4040/2=800=40*40/2=800800800800800*800*800显然太大,不可行。

    怎么降空间呢?观察题目可以发现,所有nn个木板都是要用的,即三角形的周长不变,如果知道了a,ba,b,那么第三边cc也知道了。
    因此,我们可以用f[k][i][j]f[k][i][j]表示用前kk个木板能否围成两边长为i,ji,j的三角形。
    转移时分三种情况:

    • 把第kk个木板放在ii这条边中:
      那就要用前k1k-1个木板围成ia[k]i-a[k]jj的三角形,即f[k1][ia[k]][j]f[k-1][i-a[k]][j]
    • 把第kk个木板放在jj这条边中:f[k1][i][ja[k]]f[k-1][i][j-a[k]]
    • 把第kk个木板放在第三条边中:f[k1][i][j]f[k-1][i][j]

    得到动态转移方程:

    f[k][i][j]=f[k-1][i-a[k]][j] || f[k-1][i][j-a[k]] || f[k-1][i][j];
    

    观察方程,发现了么?转移时只跟f[k1][ ][ ]f[k-1][ \ ][ \ ]这层的数据有关,所以我们完全可以去掉第一维,用原数组里的数据更新当前值f[i][j]f[i][j]
    需要注意的是,i,ji,j要倒过来循环。这个不难理解,自己思考一下就知道原因了。
    f[0][0]f[0][0]初始化为11

    最后,枚举iijj,判断能否构成三角形,若可以,用海伦公式求面积,更新答案。
    最后的最后,提醒一下求面积的函数里所有变量都要开doubledoublefloatfloat,否则只有45\text{45}分。。。别问我怎么知道的。。。

    代码实现

    #include<bits/stdc++.h>
    const int N=50;
    const int L=800+10;
    using namespace std;
    
    int n,a[N],sum;
    double ans;
    bool f[L][L];
    
    bool check(int x,int y,int z) 
    {
    	if(x+y>z&&x+z>y&&y+z>x) return 1;
    	return 0;
    }
    
    double work(double x,double y,double z)
    {
    	double p=(x+y+z)/2;
    	return sqrt(p*(p-x)*(p-y)*(p-z));
    }
    
    int main()
    {
    	int i,j,k;
    	cin>>n;
    	for(i=1;i<=n;i++){cin>>a[i]; sum+=a[i];}//用sum记录周长 
    	f[0][0]=1;
    	for(k=1;k<=n;k++)
    	  for(i=sum/2;i>=0;i--)//从周长的一半开始循环 
    	    for(j=sum/2;j>=0;j--)
    	    {
    	      if(i-a[k]>=0&&f[i-a[k]][j]) f[i][j]=1;
    	      if(j-a[k]>=0&&f[i][j-a[k]]) f[i][j]=1;
    	      //if(f[i][j]) f[i][j]=1;
              //这句可以省略 
    		}
    	ans=-1;
    	for(i=sum/2;i>0;i--)
    	  for(j=sum/2;j>0;j--)
    	  {
    	  	if(!f[i][j]) continue;
    	  	if(!check(i,j,sum-i-j)) continue;//判断能否构成三角形
    	  	ans=max(ans,work(i,j,sum-i-j));//更新答案 
    	  }
    	if(ans!=-1) cout<<(long long)(ans*100)<<endl;
    	else cout<<ans<<endl;
    	return 0;
    }
    
    • 1

    信息

    ID
    283
    时间
    1000ms
    内存
    128MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者