题目链接: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)根木棍的长度为(1leq a_ileq 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
",ans);
}
return 0;
}