题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777
题意: 给出一个N*N的数字格,第 i,j 个数字表示第 j 道题放在第 i 个位置做的得分,求最终得分超过M的做题顺序有多少种
分析: 因为N最大为12,若枚举长度为N的所有排列,一共有4亿多种,肯定会超时,所以我们会联想到状态压缩
状态:用一个长度为N的二进制串表示当前做题的状态,二进制串里的 1 的个数表示已经做了多少个题,1 的位置表示做的是第几道题,由于已经做了的题里面由于顺序不一样,所以得分也不同,我们还需要另一个维度来记录在做了同样题数的情况下的分数。即 DP[s][k]表示 做题状态为 s ,做题得分为 k 的情况下,一共有多少种做题顺序
转移方程: 首先初始化,一道题没做(s=0),一分没得(k=0)的做题方法只有1种,即DP[0][0] = 1;然后 从 s=0遍历到 (1<
for(int s=0;s<(1<<N);s++)
{
for(int j=0;j<N;j++)
{
if( (s & (1<<j))==0)
{
for(int k = 0; k <= M; ++k)
{
if(DP[s][k])
{
int tmp = MIN(k + val[p[s]][j], M);
DP[s|(1<<j)][tmp] += DP[s][k];
}
}
}
}
}
- AC代码:
/*************************************************************************
> File Name: test.cpp
> Author: Akira
> Mail: qaq.febr2.qaq@gmail.com
************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <bitset>
#include <queue>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <set>
#include <list>
#include <ctime>
#include <climits>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))
using namespace std;
#define MaxN 100000
#define MaxM MaxN*10
#define INF 0x3f3f3f3f
#define bug cout<<88888888<<endl;
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
template<typename _> inline void scan(_& t)
{
int c;
while((c = getchar()) < '0' || c > '9');
t = c - '0';
while((c = getchar()) >= '0' && c <= '9') t = t * 10 + c - '0';
}
template<typename _> inline void print(_ x)
{
int len = 0, p[20];
if(x < 0) putchar('-'), x = -x;
while(x) p[++len] = x % 10, x /= 10;
if(!len) p[++len] = 0;
while(len) putchar(p[len--] + '0');
}
int T;
int N, M;
int val[13][13];
int fac[13];
int p[1<<13];
void init()
{
fac[0] = 1;
for(int i = 1; i <= 12; i++) fac[i] = fac[i-1] * i;
for(int i=0;i<(1<<N);i++)
{
int tmp = 0;
int x = i;
while(x)
{
tmp++;
x &= (x-1);
}
p[i] = tmp;
}
}
int gcd(int a, int b) {return b == 0 ? a : gcd(b, a%b); }
int DP[(1<<13)-1][600];
void solve()
{
init();
int flag = 0;
CLR(DP);
DP[0][0] = 1;
for(int s=0;s<(1<<N);s++)
{
for(int j=0;j<N;j++)
{
if( (s & (1<<j))==0)
{
for(int k = 0; k <= M; ++k)
{
if(DP[s][k])
{
int tmp = MIN(k + val[p[s]][j], M);
DP[s|(1<<j)][tmp] += DP[s][k];
}
}
}
}
}
int ans = DP[(1<<N)-1][M];
if(ans == 0) puts("No solution");
else
{
int tmp = fac[N];
int g = gcd(ans, tmp);
printf("%d/%d\n", tmp/g, ans/g);
}
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &N, &M);
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
scanf("%d", &val[i][j]);
}
}
solve();
}
//system("pause");
}