题解 洛谷P1284 三角形牧场

做这题前首先应该了解三角形面积公式:海伦公式

这里把结论写一写,即对于任意三角形的三条边 \(a,b,c\),令 \(x=\tfrac{1}{2}(a+b+c)\),则有 \(S=\sqrt{x(x-a)(x-b)(x-c)}\)

接下来我们考虑设状态,我们令 \(f[k][a][b][c]\) 表示前 \(k\) 个木板是否能围成三边长为 \(a,b,c\) 的三角形。但是边长最大可达到 \(800\),时空复杂度 \(800^3 \times 40\),显然不可行。

于是我们尝试优化,观察发现三角形周长是不变的,那么确定两条边即可求出第三边。设 \(C\) 为三角形的周长,则我们令 \(f[k][a][b]\) 表示前 \(k\) 个木板是否能围成三边长为 \(a,b,C-a-b\)的三角形。

转移时分三种情况:

  • 把第 \(k\) 个木板放在 \(i\) 这条边中:

    那就用前 \(k-1\) 个木板围成两边长为 \(i-a[k]\)\(j\) 的三角形,即 \(f[k-1][i-a[k]][j]\)

  • 把第 \(k\) 个木板放在 \(j\) 这条边中:\(f[k-1][i][j-a[k]]\)

  • 把第 \(k\) 个木板放在第三条边中:\(f[k-1][i][j]\)

则有转移方程:

\[f[k][i][j]=\operatorname{max}(f[k-1][i][j-a[k],f[k-1][i][j-a[k]],f[k-1][i][j]) \]

此时我们继续优化空间,不难发现转移时只和 \(f[k-1][][]\) 有关,所以我们考虑去掉一维,用原数组里的数据更新当前值 \(f[i][j]\)

这里需要注意 \(i,j\) 应该倒循环,因为我们要保证 \(f[i][j-a[k]]\)\(f[j-a[k]][i]\) 的值是上一层的,与\(01\)背包类似。

最后对于 \(f[i][j]=1\)\(i,j\),我们判断\((i,j,C-i-j)\)是否可以组成三角形。若组成则用海伦公式算出面积更新答案。

\(\operatorname{Code:}\)

#include<bits/stdc++.h>
using namespace std;
const int N=45;
int n,sum,ans=-1,a[N],f[805][805];
bool check(int x,int y,int z) {
	if(x && y && z && x+y>z && y+z>x && x+z>y) return true;
	return false;
}
int hailen(double x,double y,double z) {
	double s=(x+y+z)/2;
	return sqrt(s*(s-x)*(s-y)*(s-z))*100;
}
int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum=sum+a[i];
    f[0][0]=1;
    for(int k=1;k<=n;k++) {
    	for(int i=(sum>>1);i>=0;i--) {
	    	for(int j=(sum>>1);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;
			}
		}
	}
	for(int i=0;i<=(sum>>1);i++) {
		for(int j=0;j<=(sum>>1);j++) {
		    if(f[i][j] && check(i,j,sum-i-j)) ans=max(ans,hailen(i,j,sum-i-j));
		}
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/Agonim/p/12582842.html
今日推荐