CSUST 2010-拼三角形(状压DP)

题目链接:http://acm.csust.edu.cn/problem/2010
CSDN食用链接:https://blog.csdn.net/qq_43906000/article/details/107675351
Description

Cwolf9有n根木棍,他现在想将他们拼成一些三角形,问最多能拼成多少三角形,每根木棍只能用一次。

Input
多组输入

每组数据第一行一个数\(n\), \((1 \leq n \leq 18)\)。表示有n根木棍。

接下来一行,\(n\)个数记为\(a_i\) 。表示第\(i\)根木棍的长度为\(1\leq a_i\leq 100\)

Output
对于每组数据每行输出一个数,表示用这nn根木棍可以拼出多少三角形。

Sample Input 1
6
2 2 3 4 5 6
6
2 3 8 2 3 9
5
2 3 3 4 5

Sample Output 1
2
2
1

emmm,这题可以直接暴搜DFS,但写起来很麻烦,std写的就是DFS,不过当时比赛的时候基本都是状压过的。。。。基本都是2500ms+,不过有个神仙只跑了18ms。。。。

这题能用状压?我比较菜,我也是一脸蒙逼的,只不过在yd巨巨的一番教导之下终于过了。。。2980ms+???真nm刺激

我们可以先枚举三根棍子合法想情况,并将其状态保存下来:

sort(a+1,a+1+n);
cnt=0;
for (int i=1; i<=n; i++)
	for (int j=i+1; j<=n; j++)
		for (int k=j+1; k<=n; k++) {
			if (a[i]+a[j]<=a[k]) continue;
			sta[++cnt]=(1<<(i-1))|(1<<(j-1))|(1<<(k-1));
		}

接下来我们直接枚举这些合法的情况,然后继续枚举拿棍子的状态,这种状态有(1<<n)-1种,我们是对是否拿当前的合法状态进行DP,对于枚举的拿棍状态,如果合法的三根棍子都没有被用掉,那么就可以将这个状态转移过来:

for (register int i=1; i<=cnt; i++) {
	for (register int j=0; j<(1<<n); j++) {
		if ((j&sta[i])==0) {//这三根棍子都没被用的时候
			dp[j|sta[i]]=max(dp[j|sta[i]],dp[j]+1);
			ans=max(dp[j|sta[i]],ans);
		}
	}
}

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

int a[20];
int sta[1<<19],cnt=0,dp[1<<19];

int digt(int x)
{
	int ans=0;
	while(x){
		if (x&1) ans++;
		x>>=1;
	}
	return ans;
}

int main(int argc, char const *argv[])
{
	int n;
	while (~scanf ("%d",&n)){
		memset(dp,0,sizeof dp);
		for (int i=1; i<=n; i++)
			scanf ("%d",&a[i]);
		sort(a+1,a+1+n);
		cnt=0;
		for (int i=1; i<=n; i++)
			for (int j=i+1; j<=n; j++)
				for (int k=j+1; k<=n; k++){
					if (a[i]+a[j]<=a[k]) continue;
					sta[++cnt]=(1<<(i-1))|(1<<(j-1))|(1<<(k-1));
				}
		int ans=0;
		for (register int i=1; i<=cnt; i++){
			for (register int j=0; j<(1<<n); j++){
				if ((j&sta[i])==0) {//这三根棍子都没被用的时候
					dp[j|sta[i]]=max(dp[j|sta[i]],dp[j]+1);
					ans=max(dp[j|sta[i]],ans);
				}
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/lonely-wind-/p/13399822.html