1. 程式人生 > >51nod 1406 與查詢 dp 考慮每一位 避免重復

51nod 1406 與查詢 dp 考慮每一位 避免重復

void oid nco 方法 art div tchar turn nlog

題目鏈接:

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1406

題意:

有n個整數。輸出他之中和x相與之後結果為x的有多少個。x從0到1,000,000

思路:

http://blog.csdn.net/bahuia/article/details/55653495

這題dp的思路很巧妙,分析一下題目,很容易想到,若已經求出一個數x的合法結果數,那麽對於x位數上的子集來說,x的結果對他們也同樣適用,例如,能和x=10110進行與運算能得到x的數,和x的子集y=10100進行與運算也一定能得到y。這樣就能有一個大概的思路,設dp[x]為n個數中和x進行與運算能得到x的數的個數,dp[x] = sum(dp[z]),其中x是z的子集,但是這樣的思路並不完全正確,因為計算sum(dp[z])會包含大量的重復。 舉個例子,x=10100,那麽z就有11100,10110,11110等等,註意到在計算dp[11100]的時候就已經算過了dp[11110],這裏算x的時候又同時算了dp[11100]和dp[11110],就出現了重復。所以這裏不妨每次只用比x多一位為1的數當作z,如dp[10100] = dp[11100] + dp[10110] + dp[10101]。 但是這樣的結果還是存在重復,上面dp[11100]和dp[10110]在計算的時候都會算上dp[11110],dp[11110]就重復算了兩次。 關鍵就在這一步,回憶類似背包的思想,按位來考慮,設dp[j][i]表示當前處理到第j位的時候,和i進行與運算能得到i的個數,那麽dp[j][i] = sum(dp[j-1][k]),其中i是k的子集。這樣處理就不會產生上述的重復。 當然j這一維在處理的時候可以省略,那麽方程就是dp[i] = sum(dp[k]),復雜度O(nlogn)

還有一種方法。先將每個a和x分為前k位和後20-k位兩部分,A0

表示某個a的前k位,A1表示後20-k位,同理有X0X1
設狀態:

dp[x][k]?滿A0&X0A1=X1a
轉移方程:
dp[x][k]= dp[x][k?1]+dp[x+2^k][k?1]xk0     dp[x][k?1]xk1
顯然dp[x][20]=f(x)
真難!!! 還是不理解

代碼:

代碼一:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define
PB push_back const int INF = 0x3f3f3f3f; const ll INFLL = 0x3f3f3f3f3f3f3f3fLL; inline ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();} while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();} return x*f; } void write(int x) {
int a1=0,a[20]; a[1]=0; if (!x) a1++; while (x) { a[++a1]=x%10;x/=10; } for (int i=a1;i>=1;i--) putchar(a[i]+0); } ////////////////////////////////////////////////////////////////////////// const int maxn = 1e6+1; int n,dp[maxn]; int main(){ n = read(); int mx = 0; for(int i=1; i<=n; i++){ int x = read(); dp[x]++; mx = max(mx,x); } int bit = 0; while(mx){ mx >>= 1; bit++; } for(int j=0; j<bit; j++){ for(int i=1; i<maxn; i++) if(i & (1<<j)) dp[i-(1<<j)] += dp[i]; } dp[0] = n; for(int i=0; i<maxn; i++){ write(dp[i]); puts(""); } return 0; }

代碼二:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
    return x*f;
}
void write(int x)
{
    int a1=0,a[20];
    a[1]=0;
    if (!x) a1++;
    while (x)
    {
        a[++a1]=x%10;x/=10;
    }
    for (int i=a1;i>=1;i--)
        putchar(a[i]+0);
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 1e6+1;

int n,dp[(1<<21)];

int main(){
    n = read();
    for(int i=1; i<=n; i++){
        int x = read();
        dp[x]++;
    }
    
    // for(int j=1; j<21; j++){
    //     for(int i=0; i<maxn; i++){
    //         if(i & (1<<(j-1))){
    //             dp[i] = dp[i][j-1];
    //         }else{
    //             dp[i][j] = dp[i][j-1]+dp[i+(1<<(j-1))][j-1];
    //         }
    //     }
    // }
    // 滾動優化,否則RE    
    for(int j=1; j<21; j++){
        for(int i=0; i<maxn; i++){
            if(i & (1<<(j-1))){
                dp[i] = dp[i];
            }else{
                dp[i] = dp[i]+dp[i+(1<<(j-1))];
            }
        }
    }
    
    for(int i=0; i<maxn; i++){
        write(dp[i]);
        puts("");
    }


    return 0;
}

51nod 1406 與查詢 dp 考慮每一位 避免重復