ZOJ 3777 Problem Arrangement-状压dp

http://www.icpc.moe/onlinejudge/showProblem.do?problemCode=3777


输入n,m;

给一个n*n的矩阵,mp[i][j]表示第i题选题目j会有 相应的得分

让你选择一个序列,求序列得分超过m的个数


n《12,m<=500

可以把12种状态压位到一个int

dp[i][j] 中的i有cnt个1,表示选了前cnt题,那么接下来选的是cnt+1题,

for一遍 (j=1;j<=n;j++)

如果 (1<<(j-1))&i =1表示题被选过了,不能再选


如果可以选的话,那么 dp【i+1<<(j-1)】【cur_val+ mp[ cnt+1][j] 】+= dp【i】【cur_val】;

当然为了节省空间,当cur_val+ mp[ cnt+1][j]》m,我们令其等于M即可。。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;

const double pi=acos(-1.0);
double eps=0.000001;
int min(int a,int b)
{return a<b?a:b;}
int max(int a,int b)
{return a>b?a:b;}

int gcd(int a,int b){
    if(b==0)
        return a;
	return gcd(b,a%b);
}

int n,m;
int my_popcount(int x)
{
    int tmp=0;
    for(int j=1;j<=n;j++)
                if(x&(1<<(j-1)))
                    tmp++;
                    return tmp;
}
int jie[15];
int mp[25][25];
int dp[1<<12][505];
int main()
{

    jie[1]=1;
    for(int i=2;i<13;i++)
        jie[i]=jie[i-1]*i;
	int t;cin>>t;
	while(t--)
	{
		int i,j,k;
		cin>>n>>m;
		memset(dp,0,sizeof dp);

		for (i=1;i<=n;i++)
			for (j=1;j<=n;j++)
				scanf("%d",&mp[i][j]);
			dp[0][0]=1;
			int all=1<<n;
			for (i=0;i<all;i++)		//状态
			{
				int cnt = my_popcount(i);
				for (j=1;j<=n;j++)	//选第j题
				{
					if (i&(1<<(j-1) ) ) continue;
					for (k=0;k<=m;k++)		//价值
					{
						if (dp[i][k]==0)continue;
						dp[i+(1<<(j-1))][ min(k+ mp[cnt+1][j],m) ] += dp[i][k];
					}
				}
			}
			if (!dp[(1<<n)-1][m])
				printf("No solution\n");
			else
			{
				int gd=gcd(jie[n],dp[(1<<n)-1][m]);
				printf("%d/%d\n",jie[n]/gd,dp[(1<<n)-1][m]/gd);

			}

	}




	return 0;

}




你可能感兴趣的:(ZOJ 3777 Problem Arrangement-状压dp)