小苯的排列构造
题目描述
运行代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 1000050
int i,j,k,n,m,t,a[N],b[N],f[N],l[N];
bool v[N];
int main(){
cin>>n;
for(i=1;i<=n;i++)
cin>>a[i];
v[0]=1;
for(i=1;i<=n;i++){
if(a[i-1]%a[i])
{
cout<<-1;
return 0;
}
while(v[l[a[i]]])
l[a[i]]+=a[i];
v[l[a[i]]]=1;
f[i]=l[a[i]];
}
for(i=1;i<=n;i++)
cout<<f[i]<<' ';
}
代码思路
- 读取整数序列
a[1], a[2], ..., a[n]
。 - 遍历序列,对于每个
a[i]
,确保a[i]
能够整除其前一个数a[i-1]
(除了第一个数a[1]
,因为它没有前一个数)。 - 使用一个数组
l
来存储每个数x
的当前可能位置或标签。例如,l[x]
表示当前值为x
的数应该被放置在哪个位置。 - 使用一个布尔数组
v
来标记哪些位置已经被使用了。 - 对于每个
a[i]
:如果a[i]
不能整除其前一个数(除了第一个数a[1]
),则输出-1
并终止程序。否则,在l[a[i]]
开始,向后查找第一个未被使用的位(即v[l[a[i]]]
为false
的位置)。将找到的位置标记为已使用(v[l[a[i]] + k*a[i]] = true
,其中k
是使得该位置未被使用的最小整数)。将f[i]
设置为找到的位置l[a[i]] + k*a[i]
。 - 输出所有的
f[i]
。
小苯的01背包(easy)
题目描述
运行代码
#include <iostream>
using namespace std;
const int N = 2e5+5;
int v[N],w[N];
int ans;
void solve()
{
int n,k;
cin>>n>>k;
for(int i=0;i<n;i++)
cin>>v[i]>>w[i];
for(int i=30;i>=0;i--){
int sum=ans|(1<<i);
int v_sum=(1<<30)-1;
for(int j=0;j<n;j++){
if((w[j]&sum)==sum)
v_sum&=v[j];
}
if(v_sum<=k) ans=sum;
}
cout<<ans<<endl;
return;
}
int main()
{
int f= 1;
while(f--)
solve();
return 0;
}
代码思路
-
输入处理:首先读取两个整数
n
和k
,表示物品数量和目标价值。然后读取每个物品的价值v[i]
和属性w[i]
。 -
位运算优化的动态规划:
- 外层循环从30(即二进制最高位)降到0,这是因为假设属性的位数最多为30位(由
(1<<30)-1
暗示)。 - 对于当前位
i
,计算当前最优解ans
加上这一位后的值sum
。 - 初始化
v_sum
为(1<<30)-1
,代表所有可能的属性集合。 - 遍历所有物品,检查当前物品的属性集合
w[j]
是否包含sum
的所有位(即w[j] & sum == sum
),如果是,则将该物品的价值集合(视为一个二进制位集合)与v_sum
进行按位与操作,这样可以逐步筛选出满足条件的最小价值集合。 - 如果经过上述筛选后得到的
v_sum
小于等于目标价值k
,说明加入这一位是可行的,更新当前最优解ans = sum
。
- 外层循环从30(即二进制最高位)降到0,这是因为假设属性的位数最多为30位(由
-
输出结果:最后输出得到的最大属性集合所对应的二进制值
ans
。 -
主函数:主函数中通过一个变量
f
控制解决问题的次数,这里f=1
意味着只解决一次问题,然后调用solve()
函数执行上述逻辑。
小苯的01背包(hard)
题目描述
运行代码
#include <iostream>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int read() {
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9')
f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9')
x = x * 10 + c - 48, c = getchar();
return f * x;
}
int a[N], v[N], w[N], b[N], n, k;
bool check(int x) {
int tot = 0;
for(int i = 1; i <= n; i++)
if((x & w[i]) == x)
b[++tot] = i;
int c = (1 << 31) - 1;
for(int i = 1; i <= tot; i++)
c &= v[b[i]];
if(c <= k)
return 1;
return 0;
}
signed main() {
n = read(), k = read();
for(int i = 1; i <= n; i++)
v[i] = read(), w[i] = read();
int ans = 0;
for(int i = 30; i >= 0; i--) {
if(check(ans | (1 << i)))
ans |= 1 << i;
}
cout << ans;
}
代码思路
-
输入处理:
- 使用
read()
函数自定义读取整数,支持负数,提高了输入效率。 - 读取项目数量
n
和价值阈值k
,以及每个项目的具体价值v[i]
和属性w[i]
。
- 使用
-
check函数:
- 输入一个整数
x
,表示当前考虑的属性集合。 - 遍历所有项目,如果项目
i
的属性集合w[i]
是x
的子集(即(x & w[i]) == x
),则将该项目的索引存入数组b[]
。 - 计算所有符合条件的项目价值集合的交集(通过按位与运算),并检查这个交集的值是否不大于
k
。如果满足条件,返回true
;否则,返回false
。
- 输入一个整数
-
主函数逻辑:
- 初始化答案
ans
为0,表示当前考虑的最优属性集合。 - 从最高位到最低位遍历(即从第30位到第0位),对于每一位:尝试将这一位置为1(即在当前最优解基础上增加这一位的属性),然后检查这一改变是否仍然满足总价值不超过
k
的条件,这通过调用check()
函数完成。如果满足条件,就保留这一位的修改(即这一位在最终解中应为1);如果不满足,就尝试下一位。 - 最终输出得到的属性集合的二进制表示
ans
。
- 初始化答案
利用了位运算来高效地处理集合的合并与判断,通过逐位检查的方式构建出满足条件的最优属性集合,相比直接的暴力枚举或者传统动态规划方法,在处理大规模数据时能显著提高效率。