poj2441(状压dp)

题目链接:http://poj.org/problem?id=2441,题意相当于给定n个公牛及它们各自想打的篮球场地,求取

方案数使得这n个公牛都有场地可打且这些场地均不重复。

看完《挑战程序设计竞赛》做的第一道状态压缩动态规划题,状态转移方程相对比较好想。注意这道数据

还是比较大,亦用滚动数组~~~

一开始我是用n作为第二维进行状态转移的,超时了~~~后来看了讨论,便转为m作为第二维,具体思路即:

f[S][j]表示前(j-1)个场地处理过达到状态S的方案数(其中S可压缩成一个整数,该整数的二进制位中,从右往左数

第k位若为1,表示序号为k的公牛已经有场地,否则该公牛未被分配场地。例如,若S=5,则其用二进制表示即

101,表示序号为1、3的公牛已有场地,而序号为2的未分配)

而f[S][j]=f[S][j+1](序号为j的场地不用)+f[S'][j+1](可先处理想打第j号场地的各公牛序号,然后看S,若该公牛

未被分配场地(即相应二进制位值为0),则可选将场地分配给其,然后S'即在S的基础上再将该公牛标记为

已被分配场地状态的状态)

注意对二进制位的一些基本操作:


1.求低到高位取n的第m位

int getBit(int n, int m){  
    return (n >> (m-1)) & 1;  
}  
2.从低到高位将n的第m位置为1

int setBitToOne(int n, int m){  
    return n | (1 << (m-1));  
    /*将1左移m-1位找到第m位,得到000...1...000 
      n在和这个数做或运算*/  
}  
3. 从低到高位将n的第m位置为0

int setBitToZero(int n, int m){  
    return n & ~(1 << (m-1));  
    /* 将1左移m-1位找到第m位,取反后变成111...0...1111 
       n再和这个数做与运算*/  
}  
然后边界条件即f[(1<<n)-1][m+1]=1 (所有m个场地处理过,n个公牛都被分配场地了)

再注意点循环顺序~~~我的一开始一直TLE,后来改了一下,勉强过了,10s多poj上都能过,真是开挂了!

代码如下:

import java.util.Scanner;

public class Main {
	static int n, m, num;
	static int arr[][], size[], total[], choose[][];
	static int f[];
	static boolean bo[][];

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner reader = new Scanner(System.in);
		n = reader.nextInt();
		m = reader.nextInt();
		arr = new int[n + 1][m + 1];
		size = new int[n + 1];
		bo = new boolean[n + 1][m + 1];
		for (int i = 1; i <= n; i++) {
			size[i] = reader.nextInt();
			for (int j = 1; j <= size[i]; j++) {
				arr[i][j] = reader.nextInt();
				bo[i][arr[i][j]] = true;
			}
		}
		choose = new int[m + 1][n + 1];
		total = new int[m + 1];
		for (int i = 1; i <= m; i++)
			for (int j = 1; j <= n; j++)
				if (bo[j][i]) {
					total[i]++;
					choose[i][total[i]] = j;
				}
		f = new int[1 << n];
		f[(1 << n) - 1] = 1;
		num = (1 << n) - 1;
		int num2;
		for (int i = m; i >= 1; i--) {
			for (int j = 0; j <= num; j++) {
				for (int k = 1; k <= total[i]; k++) {
					num2 = choose[i][k];
					if ((j >> (num2 - 1) & 1) == 0)
						f[j] += f[j | 1 << (num2 - 1)];
				}
			}
		}
		System.out.println(f[0]);
	}

}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值