JZOJ 4314. 【NOIP2015模拟11.4】老司机
题目
思路
大意是构造一个数组使它做 \(01\) 背包能表示出所有给定的数
那就暴力枚举每个位置填什么
直到它能表示出所有给定的数
为了保证时间复杂度
我们考虑一个二进制数 \(s\) 表示能构造出的数
\(s\) 的第 \(i\) 位为一就表示当前枚举出的数组能表示出 \(i\) 这个数
那么假如一个数就是 \(s|(s << i)|(1<<i)\)
表示每位加上 \(i\) 的数是可以被表示的
小优化:当前填到这位可以构造出给定的所有数时,给当前位打个标记
以后做到这一位就可以直接 \(return\)
对应下面的 \(b\) 数组
\(Code\)
#include<cstdio>
using namespace std;
typedef long long LL;
const int N = 25;
int n;
LL a[N] , b[N] , c[N] , anss[N] , ans = 0x3f3f3f3f;
inline void dfs(int x , LL s)
{
if (b[x - 1] || x - 1 >= ans) return;
int fl = 0;
for(register int i = 1; i <= n; i++)
if (!(s & (1LL << a[i])))
{
fl = 1;
break;
}
if (!fl)
{
ans = x - 1 , b[x - 1] = 1;
for(register int i = 1; i < x; i++) anss[i] = c[i];
return;
}
if (x > 6) return;
for(register int i = c[x - 1]; i <= 50; i++)
{
c[x] = i;
dfs(x + 1 , (s | (s << i)) | (1LL << i));
if (b[x]) return;
}
}
int main()
{
freopen("driver.in" , "r" , stdin);
freopen("driver.out" , "w" , stdout);
scanf("%d" , &n);
for(register int i = 1; i <= n; i++) scanf("%lld" , a + i);
c[0] = 1;
dfs(1 , 0);
printf("%lld\n" , ans);
for(register int i = 1; i <= ans; i++) printf("%lld " , anss[i]);
}