zoj3777(状态压缩)
生活随笔
收集整理的這篇文章主要介紹了
zoj3777(状态压缩)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目闡述:
給定n個座位,n個人,每個人可以做n個位置中的任意一個,P[i][j]代表第i個人做第j個位置獲得的分數,求有多少種排列方式使得獲得的分數大于等于M。
這道題跟數位dp的思想很像,都是窮舉可能的方式,不過數位DP由于前綴的影響可以記憶化,這道題由于n較小,可以直接狀態壓縮.
定義狀態d[i][s][t]代表已經放了i個座位,放的人數集合為s,獲得分數為t的排列的數量。
然后每次暴力枚舉每個位置可能會放的人
d[i][s | (1 << j)][t+ p[j][i]] +=d[i-1] [s] [t];
重點是如何實現:
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #define maxn 13 using namespace std; int d[1<<(maxn)][505]; //表示到達狀態s時產生的最大能量 int n,m; int P[maxn][maxn]; int ans; void init() {ans=0;memset(d,0,sizeof(d)); } //GCD //求最大公約數 //O(logn) int gcd(int a, int b) {if (b == 0)return a;elsereturn gcd(b, a%b); } int isok(int i) {int t=0;while(i){if(i&1) t++;i>>=1;}return t; } void solve() {int tot=(1<<n)-1;d[0][0]=1; //其實可以理解成d[-1][0][0],否則下面就需要特殊處理i等于0for(int i=0;i<n;i++) //代表第i個位置 {for(int s=0;s<=tot;s++) //遍歷所有狀態 {if(isok(s)!=i) continue ;//檢測哪些是前i-1個位置的狀態for(int t=0;t<=m;t++) //遍歷所有獲得的分數 {if(!d[s][t]) continue; //檢測哪些是前i-1個位置獲得的分數for(int j=0;j<n;j++) //枚舉第i個位置可能放的人 {if( s & (1<<j) ) //檢測前i-1個位置是否放過continue;int state= s | (1<<j);int MM=min(m,t+P[j][i]);d[state][MM]+=d[s][t];}}}}ans=d[tot][m]; }int main() {// freopen("test.txt","r",stdin);int fac[maxn];fac[0]=1;for(int i=1;i<maxn;i++)fac[i]=fac[i-1]*i;int t;scanf("%d",&t);while(t--){scanf("%d%d",&n,&m);init();for(int i=0;i<n;i++)for(int j=0;j<n;j++){scanf("%d",&P[i][j]);}solve();int d = gcd(fac[n], ans);if (ans == 0)printf("No solution\n");elseprintf("%d/%d\n", fac[n]/d, ans/d);}return 0; }bfs實現:
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <queue> #define maxn 13 using namespace std; int d[1<<(maxn)][505]; //表示到達狀態s時產生的最大能量 int n,m; int P[maxn][maxn]; int ans; int gcd(int a, int b) {if (b == 0)return a;elsereturn gcd(b, a%b); }int bit(int i) {int t=0;while(i){if(i&1) t++;i>>=1;}return t; } struct node {int s,t,cnt=0; }; int visit[1<<maxn][505]; void bfs() {queue < node > que;memset(visit,0,sizeof(visit));int tot=(1<<n)-1;node start,last;start.s=0; start.t=0;start.cnt=0;que.push(start);visit[start.s][start.t]=1;d[0][0]=1;while(!que.empty()){node cur=que.front();que.pop();for(int i=0;i<n;i++){if(cur.s & (1<<i))continue;node next ;next.s= cur.s | (1<<i);next.cnt=cur.cnt+1;next.t= min(m,cur.t+P[i][cur.cnt]); //將第i個人放在當前位置,然后才會加1d[next.s][next.t] += d[cur.s][cur.t];if(visit[next.s][next.t]) //保證只進隊一次continue;que.push(next);visit[next.s][next.t]=1;}}ans=d[tot][m]; }int main() {// freopen("test.txt","r",stdin);int fac[maxn];fac[0]=1;for(int i=1;i<maxn;i++)fac[i]=fac[i-1]*i;int t;scanf("%d",&t);while(t--){scanf("%d%d",&n,&m);ans=0;memset(d,0,sizeof(d));;for(int i=0;i<n;i++)for(int j=0;j<n;j++){scanf("%d",&P[i][j]);}bfs();int d = gcd(fac[n], ans);if (ans == 0)printf("No solution\n");elseprintf("%d/%d\n", fac[n]/d, ans/d);}return 0; }?
轉載于:https://www.cnblogs.com/xianbin7/p/4780565.html
總結
以上是生活随笔為你收集整理的zoj3777(状态压缩)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到马哭了是什么意思
- 下一篇: 算法学习之选择排序