1. 程式人生 > >zoj 3777 狀壓dp || 二分+搜索

zoj 3777 狀壓dp || 二分+搜索

oid 矩陣 vector code print 上下 span 1的個數 c++

題意:給一個矩陣(n*n n<=14)求出選擇矩陣不同行,不同列,最後加起來和大於m的選擇數

狀壓dp做法:

由於每一行都要選擇,那麽認為就是從第一行開始順序選擇

。那麽一個二進制數,它的1的個數就是選擇了的行數,而每個位置的1代表了這一個列選擇或則不選擇

這樣就用一個二進制數表示出來狀態了。

dp[i][j]中的i代表此時的選擇狀態,j是和dp[i][j]表示和為j的選擇數有多少個。

確立的狀態表示的方法之後,狀壓dp就比較簡單了

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define
pb push_back #define mp make_pair #define fi first #define se second #define all(v) v.begin(),v.end() #define mem(a) memset(a,0,sizeof(a)) const int N = 2e5+4; const ll mod =1e9+7; const int INF = 1e9+4; const double eps = 1e-7; int d[1<<13][600]; int p[522][555]; int n,m; int gcd(int a,int b){
return b==0?a:gcd(b,a%b); } int fac[22]; int main(){ int t; fac[0]=1; for(int i=1;i<=13;++i) fac[i] =fac[i-1]*i; cin>>t; while(t--){ memset(d,0,sizeof(d)); cin>>n>>m; for(int i=1;i<=n;++i){ for(int j=1;j<=n;++j)scanf("
%d",&p[i][j]); } d[0][0]=1; for(int i=0;i<=(1<<n);++i){ int row = 0; for(int k=1;k<=n;++k){ if( (i>> (k-1) )&1) row++; } for(int j=1;j<=n;++j){ if( (i>>(j-1))&1)continue; for(int k=0;k<=m;++k){ if(k+p[row+1][j]>=m) d[i+ (1<<(j-1))][m]+=d[i][k]; else d[i+(1<<(j-1))][k+p[row+1][j]]+=d[i][k]; } } } if( d[(1<<n)-1][m] == 0) printf("No solution\n"); else{ int tm = gcd(fac[n],d[(1<<n)-1][m]); printf("%d/%d\n",fac[n]/tm, d[(1<<n)-1][m]/tm); } } return 0; }

當時做題並沒想到狀壓dp,看到了14直接分成上下兩半,搜索,然後遍歷上面的解,在下面的解中找 >=m-a[i]的

這個還要註意用二進制表示一下不同列,這樣才能找。過的比較極限...

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=15;
const int maxm=505;
const int N =1e6+4;
bool row[maxn],col[maxn];
int p[maxn][maxn];

int n,m;
int fa[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096};
ll gcd(ll a,ll b){
    if(a==0)return b;
    if(b==0)return a;
    if(!(a&1)&&!(b&1))
        return gcd(a>>1,b>>1)<<1;
    else if(!(b&1))
        return gcd(a,b>>1);
    else if(!(a&1))
        return gcd(a>>1,b);
    else
        return gcd(abs(a-b),min(a,b));
}

vector<int> b[665290];

bool vis[665290];
int tmp[20];
int cnt;
int num;ll fenzi=1,fenmu=0;
int sum;vector<int>use;

ll ares=1;
ll bres=1;
void adfs(int x,int y){
    if(x== n/2){
        if(vis[ares]==false){
            use.push_back(ares);
            sort(b[ares].begin(),b[ares].end());vis[ares]=true;
        }
        fenmu+= b[ares].end()-lower_bound(b[ares].begin(),b[ares].end(),m-sum);
        return ;
    }
    for(int i=1;i<=n;++i){
        if(col[i])continue;
        col[i]=true;
        ares-=fa[i];
        sum+=p[x+1][i];
        adfs(x+1,i);
        sum-=p[x+1][i];
        col[i]=false;
        ares+=fa[i];

    }
}

void bdfs(int x,int y){
    if(x==n){
        b[bres].push_back(sum);
        return ;
    }

    for(int i=1;i<=n;++i){
        if(col[i])continue;
        col[i]=true;
        bres+=fa[i];
        sum+=p[x+1][i];
        bdfs(x+1,i);
        sum-=p[x+1][i];
        col[i]=false;
        bres-=fa[i];
    }
}


void Cl(){
    for(int i=0;i<use.size();++i)
        b[use[i]].clear(),vis[use[i]]=false;

    use.clear();
    num = 0;
    sum = 0;
    cnt =0;
}

int main(){
    int T;scanf("%d",&T);
    while(T--){
        num = 0;
        scanf("%d%d",&n,&m);
        if(n==1){
            int x;
            scanf("%d",&x);
            if(x>=m){
                printf("1/1\n");
            }else {
                printf("No solution\n");
            }
            continue;
        }
        Cl();
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j)
                scanf("%d",&p[i][j]);
        }
        fenzi=1,fenmu=0;

        for(ll i=1;i<=n;++i)fenzi*=i;
        //b
        int ss=n/2+1;
        bres=0;
        for(int i=1;i<=n;++i){
            sum = p[ss][i];
            bres = fa[i];
            col[i]=true;
            bdfs(ss,i);
            col[i]=false;
            bres -= fa[i];
        }

        //a
        ares =0;
        for(int i=1;i<=n;++i) ares+=fa[i];
        for(int i=1;i<=n;++i){
            sum = p[1][i];
            ares-=fa[i];
            col[i]=true;
            adfs(1,i);
            col[i]=false;
            ares+=fa[i];
        }
        cnt= 0;

        bool ok= false;
        if(fenmu==0)ok=false;
        else ok =true;
        if(ok==false){
            printf("No solution\n");
            continue;
        }
        ll chu=gcd(fenzi,fenmu);
        ll fenmuu = fenmu /chu;
        ll fenzii = fenzi /chu;
        printf("%lld/%lld\n",fenzii,fenmuu);
    }
    return 0;
}

zoj 3777 狀壓dp || 二分+搜索