UVALive-2965 && 网易19-牛牛的背包问题(中途相遇法思想)

UVALive-2965题目链接:https://vjudge.net/problem/UVALive-2965
题意:给n个大写字母构成的字符串,要求选取若干个字符串,满足每个字母出现偶数次(即选取的字符串集合中A-Z总数出现偶数次)

思路:n有24,直接暴力选(每个串选或者不选暴力,见下题有类似实现)是TLE。
所以采用将n个字符串一分为2,再逐个暴力,复杂度就变得可接受了。

由于本题只考虑A-Z出现的奇偶,所以先将每个字符串映射为数字(26位,A在最低位,Z在最高为),方便我们异或操作。
那么问题转化成 在我们划分的2个字符串集合中,选出两个子集(两个数),这两个数异或结果为0(每个字母出现偶数次,偶数次异或即为0)
因此,我们枚举并存储在第一个集合选取的结果,枚举第二个集合时,只需判断结果是否出现过(第一次是否枚举到),若出现,两数异或结果为0。同时,我们取选取字符串多的即可。

Code:

#include <bits/stdc++.h>
using namespace std;
const int AX = 1e3 + 66 ; 
char s[AX];
int a[30] ; 
int cal(int x){  //计算x二进制形式中1的个数
	return !x ? 0 : cal( x >> 1 ) + ( x & 1 ) ;
}
int main(){
	int n ;
	while( ~scanf("%d",&n) && n ){
		map<int,int> mp ; 
		memset( a , 0 , sizeof(a) );
		for( int i = 0 ; i < n ; i++ ){
			scanf("%s",&s);
			int len = strlen(s) ; 
			for( int j = 0 ; j < len ; j++ ){
				a[i] |= ( 1 << (int)( s[j] - 'A' ) ) ;
			}
		}
		int len1 = n / 2 ; 
		int len2 = n - len1 ; 
		for( int i = 0 ; i < ( 1 << len1 ) ; i++ ){  //枚举第一个集合中选取字符串的所有情况
			int tmp = 0 ;
			for( int j = 0 ; j < len1 ; j++ ){
				if( ( 1 << j ) & i ){
					tmp ^= a[j] ; 
				}
			}
			if( !mp.count(tmp) || cal(i) > cal(mp[tmp]) ){ //1的个数即为选取的字符串数
				mp[tmp] = i ; 
			}
		}
		int res = 0 ; 
		for( int i = 0 ; i < ( 1 << len2 ) ; i++ ){
			int tmp = 0 ;
			for( int j = 0 ; j < len2 ; j ++ ){
				if( ( 1 << j ) & i ){
					tmp ^= a[j+len1] ; 
				}
			}
			if( mp.count(tmp) && cal(i) + cal(mp[tmp]) > cal(res) ){ //tmp在第一次出现过,相同数异或得到0
				res = ( i << len1 ) ^ mp[tmp] ;   //将两个集合合并到res中
			}
		}
		printf("%d\n",cal(res));	
		for(int i = 0; i < n; i++){
			if( res & ( 1 << i ) ) printf("%d ",i+1);	
		}
		printf("\n");
	}
	return 0 ; 
}

网易19校招–牛牛的背包问题

题目描述
牛牛准备参加学校组织的春游, 出发前牛牛准备往背包里装入一些零食, 牛牛的背包容量为w。
牛牛家里一共有n袋零食, 第i袋零食体积为v[i]。
牛牛想知道在总体积不超过背包容量的情况下,他一共有多少种零食放法(总体积为0也算一种放法)。
输入描述:
输入包括两行
第一行为两个正整数n和w(1 <= n <= 30, 1 <= w <= 2 * 10^9),表示零食的数量和背包的容量。
第二行n个正整数v[i](0 <= v[i] <= 10^9),表示每袋零食的体积。
输出描述:
输出一个正整数, 表示牛牛一共有多少种零食放法。
示例1
输入
复制
3 10
1 2 4
输出
复制
8
说明
三种零食总体积小于10,于是每种零食有放入和不放入两种情况,一共有222 = 8种情况。

这题我开始想是背包,看到w是2e9,肯定就不行了,n是30,就想到了dfs爆搜,但是一直不敢动手,n是30感觉也是T,最后实在没辙,就试了下没想到过了,而且是6ms,看了评论区讲是中途相遇法,就又写了一种解法。(见下

dfs Code:

#include <bits/stdc++.h>
using namespace std;
const int AX = 30 + 6;
long long v[AX] ; 
int n ;
long long w ;
int res = 0 ; 
map<int,int>mp ;
void dfs( int x , long long sum , int sta ){
    if( x == n ){
        if( !mp[sta] ) { res ++ ; mp[sta] = 1 ; } 
        return ; 
    }
    dfs( x + 1 , sum , sta ) ; 
    if( sum + v[x] <= w ){
        dfs( x + 1 , sum + v[x] , sta |= ( 1 << x ) ) ; 
    }
}

int main(){
    long long ans = 0 ; 
    cin >> n >> w ;
    for( int i = 0 ; i < n ; i++ ){
        cin >> v[i] ; 
        ans += v[i] ;
    }
    if( ans <= w ) res = 1 << n ; 
    else dfs( 0 , 0 , 0 ) ;
    cout << res << endl ; 
    return 0 ; 
}

中途相遇法:

思路:将n袋食物分成两半,然后枚举第一份所有集合,存储和<=w的,存储的和升序排序
再枚举第二份所有子集(假设子集和为tmp),二分第一次存储的满足的集合,找出第一个大于等于w-tmp的位置pos,res += pos + 1 即可。
这样这边的复杂度就从2^30 到 2^15 + 2 ^15了


#include <bits/stdc++.h>
using namespace std;
const int AX = 30 + 6;
long long v[AX] ; 
int n , w ; 
struct Node{
	long long sum ; 
	int rec ;
	Node(){}
	Node( int sum , int rec ):sum(sum),rec(rec){}
	bool operator < ( const Node &b )const{
		return sum < b.sum ; 
	}
};
int cal(int x){
	return !x ? 0 : cal( x >> 1 ) + ( x & 1 ) ;
}
vector<Node>vec ;
int Binary_search( int x ){
	int l = 0 ; 
	int r = vec.size() - 1 ; 
	while( l <= r ){
		int mid = ( l + r ) >> 1 ; 
		if( vec[mid].sum >= x ) r = mid - 1; 
		else l = mid + 1 ; 
	}
	return l ; 
}
int main(){
   
    cin >> n >> w ;
    for( int i = 0 ; i < n ; i++ ){
        cin >> v[i] ; 
    }
	int len1 = n / 2 ; 
	int len2 = n - len1 ; 
	
	long long res = 0 ;
	
	
	for( int i = 0 ; i < ( 1 << len1 ) ; i++ ){
		long long tmp = 0 ; 
		for( int j = 0 ; j < len1 ; j++ ){
			if( i & ( 1 << j ) ){
				tmp += v[j] ; 
			}
		}
		if( tmp <= w ) { vec.push_back(Node(tmp,i)) ; }
	}
	sort( vec.begin() , vec.end() );
	
    for( int i = 0 ; i < ( 1 << len2 ) ; i++ ){
		long long tmp = 0 ; 
		for( int j = 0 ; j < len2 ; j++ ){
			if( i & ( 1 << j ) ){	
				tmp += v[j+len1] ;
			}
		}
		if( tmp <= w ){
			int pos = Binary_search( w - tmp ) ; 
			if( pos >= 0 && pos < vec.size() && vec[pos].sum + tmp <= w ){ 
				res += pos + 1 ;
			}else if( pos - 1 >= 0 && pos - 1 < vec.size() && vec[pos-1].sum + tmp <= w ){
				pos -- ; 
				res += pos + 1 ;
			}
		}
	}
	cout << res << endl ; 
    return 0 ; 
}

猜你喜欢

转载自blog.csdn.net/FrankAx/article/details/104201767