zoj 3777 状压dp || 二分+搜索
Posted wjhstudy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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; }
以上是关于zoj 3777 状压dp || 二分+搜索的主要内容,如果未能解决你的问题,请参考以下文章
ZOJ-3777 Problem Arrangement(状态压缩DP)
ZOJ-3777-Problem Arrangement(状态DP)
B - Problem Arrangement ZOJ - 3777