题目阐述:
给定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; }