Poj 2923 Relocation (DP_状态压缩DP(背包))

题目链接:http://poj.org/problem?id=2923


题目大意:给定n个物品和两辆车的最大运载量,每次两辆车都要同时开动,问最少开几次能把所有物品运走,1<=n<=10.


解题思路:好题,解法为状态压缩DP+背包,我的状态压缩DP做的特别少,所有在看到本题的n的范围时还没能很敏感地往状态压缩方面想。本题的解题思路是先枚举选择若干个时的状态,总状态量为1<<n,判断这些状态集合里的那些物品能否一次就运走,如果能运走,那就把这个状态看成一个物品。预处理完能从枚举中找到tot个物品,再用这tot个物品中没有交集(也就是两个状态不能同时含有一个物品)的物品进行01背包,每个物品的体积是state[i],价值是1,求包含n个物品的最少价值也就是dp[(1<<n)-1](dp[i]表示状态i需要运的最少次数)。

    状态转移方程:dp[j|k] = min(dp[j|k],dp[k]+1) (k为state[i,1<=j<=(1<<n)-1])。算法复杂度O((2^N)*N)


测试数据:

10
6 10 10
10 10 10 10 10 10

6 2 4
1 1 2 2 3 3

6 12 13
3 9 13 3 10 11

7 1 100
1 2 33 50 50 67 98


代码:

#include <stdio.h>
#include <string.h>
#define MAX 2100
#define INF 0xfffffff
#define min(a,b) (a)<(b)?(a):(b)


int state[MAX],dp[MAX];
int n,m1,m2,ans,tot,cost[MAX];


int Ok(int x) {

	int i,j,k,sum = 0;
	int  vis[MAX];


	memset(vis,0,sizeof(vis));
	for (vis[0] = 1,i = 0; i < n; ++i)
		if ((1<<i) & x) {

			sum += cost[i];
			for (j = m1; j >= cost[i]; --j)
				if (vis[j-cost[i]]) vis[j] = 1;
		}


	for (i = 0; i <= m1; ++i)
		if (vis[i] && sum - i <= m2) 
			return 1;
	return 0;
}


int main()
{
	int i,j,k,t,cas = 0;


	scanf("%d",&t);
	while (t--) {

		scanf("%d%d%d",&n,&m1,&m2);
		for (i = 0; i < n; ++i)
			scanf("%d",&cost[i]);	
		

		for (i = 0; i < 1<<n; ++i) dp[i] = INF;
		dp[0] = tot = ans = 0;
		for (i = 1; i <= (1<<n) - 1; ++i)
			if (Ok(i)) state[tot++] = i;


		for (i = 0; i < tot; ++i)
			for (j = (1<<n)-1; j >= 0; --j) {

				if (dp[j] == INF) continue;
				int tp = j + state[i];
				if (tp != (j | state[i])) continue;
				dp[tp] = min(dp[tp],dp[j]+1);
			}


		printf("Scenario #%d:\n%d\n\n",++cas,dp[(1<<n)-1]);
	}
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

你可能感兴趣的:(算法,测试)