题目链接:​​http://acm.csust.edu.cn/problem/2010​

​https://blog.51cto.com/u_15249461/2848670​​​

Description

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

Input

多组输入

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

接下来一行, n n n个数记为 a i a_i ai​ 。表示第 i i i根木棍的长度为 1 ≤ a i ≤ 100 1\leq a_i\leq 100 1≤ai​≤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;
}