HDU 5119 Happy Matt Friends(DP)

本文介绍了一道经典的异或和问题,通过动态规划的方法解决给定数字集合中任意子集的异或和大于等于特定值的组合数量。详细解析了状态转移方程及实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5119

题目大意:给定n个数字(范围在10^6之内),给定一个m;求在这n个数字当中取出任意的数字并求异或和,值需要大于等于m的组合一共有多少种。

解析:
首先,利用动态规划的方法,首先定义 dp[i][j] 为取到第 i 个数字时其异或和为 j 的可能的取法的种数;那么状态转移方程变为 dp[i][j] = dp[i-1][j] + dp[i-1][j^arr[i]] ; 在第 i 个数字的时候考虑其能达到异或和为 j 的可能性为: 1、不取第 i 个数字, 异或和为 j 的可能性为 dp[i-1][j]。2、取第 i 个数字, 异或和为 j 的可能性为dp[i-1][j^arr[i]]。

其次,初始时, 应将dp[0][0]初始化为1。由于n个数字中可能最大的数为1000000,在二进制下为1111 0100 0010 0100 0000 即:可能得到的最大的异或值为 1111 1111 1111 1111 1111 (十进制下)1048575。

知识点: 二进制异或运算的性质:
1、相同取0, 不同取1。
2、 a ^ b ^ a = b 即: arr[i]^x = j => arr[i]^j = x.


AC代码如下:

#include <iostream>
#pragma comment(linker, "/STACK:1024000000,1024000000") 
#include <stdio.h>
#include <fstream>
#include <iomanip>
#include <cmath>
#include <string>
#include <string.h>
#include <sstream>
#include <cctype>
#include <climits>
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <vector>
#include <iterator>
#include <algorithm>
#include <stack>
#include <functional>
//cout << "OK" << endl;
#define _clr(x,y) memset(x,y,sizeof(x))
#define _inf(x) memset(x,0x3f,sizeof(x))
#define pb push_back
#define mp make_pair
#define FORD(i,a,b) for (int i=(a); i<=(b); i++)
#define FORP(i,a,b) for (int i=(a); i>=(b); i--)
#define REP(i,n) for (int i=0; i<(n); i++)
using namespace std;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
const double EULER = 0.577215664901532860;
const double PI = 3.1415926535897932384626;
const double E = 2.71828182845904523536028;
typedef long long LL;
LL pow_mod(LL a,LL n,LL m)
{
    if(n == 0) return 1;
    LL x = pow_mod(a,n>>1,m);
    LL ans = x*x%m;
    if(n&1) ans = ans*a%m;
    return ans;
}
int gcd(int a,int b){return b == 0 ? a : gcd(b,a%b);}

int arr[50];
LL dp[50][1048580];//1048580大于可能得到的最大的异或和的值  最大可能值为1048575 
int main(){
    int t;
    cin >> t;
    int cont = 1;
    while(t--){
        int n, m;
        scanf("%d%d",&n,&m);
        memset(dp,0,sizeof(dp));
        memset(arr,0,sizeof(arr));
        for(int i = 1; i <= n; i++){
            scanf("%d",&arr[i]);
        }
        dp[0][0] = 1;
        for(int i = 1; i <= n; i++){
            for(int j = 0; j < 1048576; j++){//注意这里dp的范围要在最大可能的范围之内
                dp[i][j] = dp[i-1][j] + dp[i-1][j^arr[i]];
                //cout << dp[i][j] << " ";
            } 
            //cout << endl;
        }
        LL ans = 0;
        for(int j = m; j < 1048576; j++){
            ans += dp[n][j];
        }
        printf("Case #%d: %lld\n",cont++,ans);
    }
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值