hdu 4901 The Romantic Hero (dp+背包问题)

来源:互联网 发布:欧冠足球2 球员数据 编辑:程序博客网 时间:2024/06/06 04:04

题意:

有n个数,从n个数中选出两个集合s和集合t,保证原序列中,集合s中的元素都在

集合t中元素的左边。且要求集合s中元素做抑或运算的值与集合t中元素做与运算的

值相等。问能选出多少种这样的集合s和t。


算法:

左右dp。

用dp[i][j]表示前i个数 做抑或运算得到j的方法数。最后一个值取不取到都不一定。

故为背包的问题。右边也是一样。

枚举时可能出现重复。枚举到第i个和枚举第i+1个可能重复。所以要枚举一个中间值。

这个中间值是归到s集的,因为抑或支持逆运算,而与是不支持的。

所以最后dp方程应该改为  前i个数一定包含第i个做抑或得到值j的方法数,即s[i][j]。

那么s[i][j] = dp[i-1][j^a[i]]。


右边也是做与运算,但要注意下标。


然后取long long应该在乘法之前,否则乘法运算后可能已经溢出,再做long long 就没意义了。


#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int mod = 1000000000+7;typedef long long ll;int a[1010];int dp[1010][1025],dp1[1010][1025],s[1010][1025];int main(){    int T,n;    scanf("%d",&T);    while(T--)    {        scanf("%d",&n);        for(int i=1;i<=n;i++)            scanf("%d",&a[i]);        memset(dp,0,sizeof(dp));        dp[0][0] = 1;        for(int i=1;i<=n;i++)        {            for(int j=0;j<1024;j++)            {                dp[i][j] = dp[i-1][j]+dp[i-1][j^a[i]];                if(dp[i][j]>=mod)                    dp[i][j] -= mod;            }            for(int j=0;j<1024;j++)                s[i][j] = dp[i-1][j^a[i]];        }        memset(dp1,0,sizeof(dp1));        for(int i=n;i>=1;i--)        {            dp1[i][a[i]]++;            for(int j=0;j<1024;j++)            {                dp1[i][j&a[i]] = (dp1[i][j&a[i]]+dp1[i+1][j])%mod;                dp1[i][j] = (dp1[i][j]+dp1[i+1][j])%mod;            }        }        int cnt = 0;        for(int k=1;k<=n-1;k++)        {            for(int j=0;j<1024;j++)            {                cnt = (cnt+(ll)s[k][j]*dp1[k+1][j])%mod;                if(cnt>=mod) cnt-=mod;            }        }        printf("%d\n",cnt);    }    return 0;}




0 0
原创粉丝点击