题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5264

题意:

给定一个n*n的矩阵,在矩阵中选n个数字(每行每列只能选一个数字),问选出的数字和>=m的方法数

思路:

1、矩阵较小可以状压

2、每次转移以 数字和为记录,因为m<=500,对于数字和>m,可以视为m


相当于一个全排列中找方法数

即把n个数填入n个空格内。

则对于第i个数,我们不需要关心前面i-1个数所在的各个位置,只需要知道他们占了哪些位置

也就是 对于开始时 状态为 n个0,加入一个数可以转移到n位 二进制个数为1的状态。

一个优化是滚动,另一个是去掉前一个状态方法数为0的状态
dp[i][j][k] 表示前i件物品,在摆成二进制j状态下 和为k 的方法数

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<vector>
#include<queue>
#include<set>
using namespace std;
#define N 12
#define ll int
ll n, m;
ll dp[2][5000][501];
ll mp[N][N];
ll Jie[20];
vector<ll>G[13];
ll Siz[13];
ll GCD(ll x,ll y){
	if(x>y)swap(x,y);
	while(x){
		y %= x;
		if(x>y)swap(x,y);
	}
	return y;
}
ll siz(ll x){ ll ans = 0; while(x){ans += x&1;x>>=1;} return ans; }
bool use[2][5000];
void init(ll x){memset(dp[x], 0, sizeof dp[x]);memset(use[x], 0, sizeof use[x]);}
int main(){
	int T;scanf("%d",&T);
	ll i, j;
	Jie[0] = 1;
	for(i = 1; i <=12; i++)Jie[i] = Jie[i-1]*i;
	for(i = 0; i <=12; i++)G[i].clear();
	for(i = 0; i <(1<<12); i++)	G[siz(i)].push_back(i);
	for(i = 0; i <= 12; i++)Siz[i] = G[i].size();
	while(T--){
		scanf("%d %d", &n, &m);
		for(i = 0; i < n; i++)	for(j = 0; j < n; j++)	scanf("%d",&mp[i][j]);
		init(0);
		ll cur = 0, top = 1, D = (1<<n);
		for(i = 1, j = 0; i < D; i<<=1, j++)
		{
			dp[0][i][min(m,mp[0][j])] = 1;
			use[cur][i] = 1;
		}
		for(i = 1; i < n; i++)
		{
			cur ^= 1;
			init(cur);
			for(j = 0; j < Siz[top]; j++)
			{
				ll now = G[top][j];
				if(now>=D)break;
				if(!use[cur^1][now])continue;
				for(ll z = 0; z < n; z++)if((now & (1<<z)) ==0)
				{
					ll t = now | (1<<z);
					bool yes = false;
					for(ll d = 0; d <= m; d++)
						if(dp[cur^1][now][d])
						{
							dp[cur][t][min(d + mp[i][z], m)] += dp[cur^1][now][d];
							yes = true;
						}
						use[cur][t] |= yes;
				}
			}
			top++;
		}
		if(dp[cur][(1<<n)-1][m]==0)puts("No solution");
		else
		{
			ll gcd = GCD(dp[cur][(1<<n)-1][m], Jie[n]);
			printf("%d/%d\n",Jie[n]/gcd, dp[cur][(1<<n)-1][m]/gcd);
		}
	}
	return 0;
}
/*
99
3 10
2 4 1
3 2 2
4 5 3
2 6
1 3
2 4
12 500
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 



*/