zoukankan      html  css  js  c++  java
  • zoj 3777 状压dp || 二分+搜索

    题意:给一个矩阵(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
    ");
            else{
                int tm = gcd(fac[n],d[(1<<n)-1][m]);
                printf("%d/%d
    ",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
    ");
                }else {
                    printf("No solution
    ");
                }
                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
    ");
                continue;
            }
            ll chu=gcd(fenzi,fenmu);
            ll fenmuu = fenmu /chu;
            ll fenzii = fenzi /chu;
            printf("%lld/%lld
    ",fenzii,fenmuu);
        }
        return 0;
    }
  • 相关阅读:
    Smarty模板引擎技术(三)
    Smarty模板引擎技术(二)
    Smarty模板引擎技术(一)
    Ajax技术
    JavaScript--XML DOM
    JavaScript--HTML DOM
    [转]常用正则表达式
    JavaScript--事件
    CentOS 下开启PHP错误提示
    JavaScript实例
  • 原文地址:https://www.cnblogs.com/wjhstudy/p/10305962.html
Copyright © 2011-2022 走看看