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)]
然后枚举f1i
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 但不知道为什么跑不出来。。。

posted @ 2023-11-10 16:00  N0zoM1z0  阅读(19)  评论(0编辑  收藏  举报