UDCTF2023 appetizer
chall.py
#Subset Sum Problem
#https://imgs.xkcd.com/comics/np_complete.png
import random
choices = list(set([random.randint(100000,1000000000) for _ in range(30)]))
random.shuffle(choices)
winners = choices[:15]
target = sum(winners)
winners.sort()
flag = "UDCTF{%s}" % ("_".join(map(str,winners)))
#print(flag)
choices.sort()
print(choices)
print(target)
太久不写C++的算法 手太生了。。。写了半天。。。
思路就是将30个数分成两部分 每部分15个
然后用子集枚举分别算出f1[0~(1<<15)] f2[0~(1<<15)]
然后枚举f1
的i
将f2
排序后用二分查找找到对应的f2[]
值然后遍历f2
找到下标
这样f1 f2
的下标都找到了 然后还原对应的chioces[]
项即可
solution.c
#include<bits/stdc++.h>
using namespace std;
#define int long long
int f1[(1<<15)+10],f2[(1<<15)+10];
signed main(){
int choices[] = {19728964, 30673077, 137289540, 195938621, 207242611, 237735979, 298141799, 302597011, 387047012, 405520686, 424852916, 461998372, 463977415, 528505766, 557896298, 603269308, 613528675, 621228168, 654758801, 670668388, 741571487, 753993381, 763314787, 770263388, 806543382, 864409584, 875042623, 875651556, 918697500, 946831967};
int c1[20],c2[20];
for(int i=0;i<=14;i++)c1[i]=choices[i];
for(int i=0;i<=14;i++)c2[i]=choices[i+15];
int target = 7627676296;
int sum=0;
// for(int i=0;i<14;i++)sum+=choices[i];
// cout<<sum<<"\n";
for(int i=0;i<(1<<15);i++){
int s=0;
for(int j=0;j<=14;j++){
int x=(i&(1<<j)&((1<<i)-1));
// cout<<i<<","<<j<<","<<x<<"\n";
if((x)){
s+=c1[j];
}
}
f1[i]=s;
}
// cout<<f1[(1<<14)-1]<<"\n\n\n";
// for(int i=0;i<10;i++)cout<<f1[i]<<",";
// for(int i=0;i<10;i++)
// cout<<c1[i]<<",";cout<<"\n\n\n";
// for(int i=0;i<10;i++)
// cout<<f1[i]<<",";
for(int i=0;i<(1<<15);i++){
int s=0;
for(int j=0;j<=14;j++){
int x=(i&(1<<j)&((1<<i)-1));
// cout<<i<<","<<j<<","<<x<<"\n";
if((x)){
s+=c2[j];
}
}
f2[i]=s;
}
// for(int i=0;i<10;i++)
// cout<<c2[i]<<",";cout<<"\n\n\n";
// for(int i=0;i<10;i++)
// cout<<f2[i]<<",";
// cout<<"\n\n";
int pos[]={0,1,2,3,5,7,12};
for(int x:pos){
cout<<c1[x]<<",";
}
// for(int i=0;i<(1<<15);i++)
// if(f2[i]==6239735689)
// {
// cout<<i<<"\n";
// break;
// }
// sort(f2+0,f2+(1<<15));
// for(int i=0;i<(1<<15);i++){
// int res = target-f1[i];
// int pos=lower_bound(f2,f2+(1<<15),res)-f2;
// if(f2[pos]==res){
// cout<<i<<"\n";
// cout<<f1[i]<<"\n";
// cout<<f2[pos]<<"\n";
// break;
// }
// }
}
看来OI偶尔还是要做做 不然算法真的都不会写了。。。(写个类状压的子集枚举还出了一堆bug...)
贴一个官方给的做法:
def subset_sum(numbers, target, partial=[]):
s = sum(partial)
# If the sum of the current subset equals the target, print the flag
if s == target:
flag = "UDCTF{%s}" % ("_".join(map(str, partial)))
print("Flag:", flag)
# If the sum of the current subset exceeds the target, stop exploring this path
if s >= target:
return
# Recursively try including and excluding elements from the subset
for i in range(len(numbers)):
remaining = numbers[i+1:]
subset_sum(remaining, target, partial + [numbers[i]])
# List of numbers and target sum
choices = [19728964, 30673077, 137289540, 195938621, 207242611, 237735979, 298141799, 302597011, 387047012, 405520686, 424852916, 461998372, 463977415, 528505766, 557896298, 603269308, 613528675, 621228168, 654758801, 670668388, 741571487, 753993381, 763314787, 770263388, 806543382, 864409584, 875042623, 875651556, 918697500, 946831967]
target = 7627676296
# Call the subset_sum function to find and print the flag
subset_sum(choices, target)
话说最近学了lattice再来看这道不知为什么背包爆不出来吗?思路就是给格加一维约束Σxi= 15 但不知道为什么跑不出来。。。