状态压缩DP-Hie With the Pie(POJ 3311)

Hiewith the Pie

Time Limit: 2000MS

Memory Limit: 65536K

Total Submissions: 3571

Accepted: 1859

Description

The Pizazz Pizzeria prides itself in delivering pizzas to itscustomers as fast as possible. Unfortunately, due to cutbacks, they can affordto hire only one driver to do the deliveries. He will wait for 1 or more (up to10) orders to be processed before he starts any deliveries. Needless to say, hewould like to take the shortest route in delivering these goodies and returningto the pizzeria, even if it means passing the same location(s) or the pizzeriamore than once on the way. He has commissioned you to write a program to helphim.

Input

Input will consist of multiple test cases. The first line willcontain a single integer n indicating the number of orders todeliver, where 1 ≤ n ≤ 10. After this will be n + 1 lines each containing n + 1 integers indicating the times totravel between the pizzeria (numbered 0) and the n locations (numbers 1 to n). The jth value on the ith line indicates the time togo directly from location i to location jwithout visiting any otherlocations along the way. Note that there may be quicker ways to go from i to j via other locations, due to differentspeed limits, traffic lights, etc. Also, the time values may not be symmetric,i.e., the time to go directly from location i to j may not be the same as the time to godirectly from location j to i.An input value of n = 0 will terminate input.

Output

For each test case, you should output a single number indicatingthe minimum time to deliver all of the pizzas and return to the pizzeria.

Sample Input

3
0 1 10 10
1 0 1 2
10 1 0 10
10 2 10 0
0

Sample Output

8

Source

East Central North America 2006

 

【题目大意】一个送pizza的外卖员,要送去多个地点,已知这些节点的两两距离,问如果选择路径,能够送完所有的外卖且走过的距离最短,(其中相同的地方可以路过多次)

 

【分析】不妨用floyd算法求出它们两两之间的最短距离,然后用状态压缩的办法状态数组dp[1<<N][N+1];表示状态和当前的地方id;

 

【状态表示】dp[i][j] 表示在处于状态i(由路过的节点集合)的时候,且当前节点为j的路径值; 

【状态转移方程】dp[state][j] = dp[state’][j]+dis[j][k]

【边界方程】:dp[state][j]  = dis[0][j];其中state为1>>(j-1)

注意:第一次碰到这种类型的题目,比较麻烦的是它的边界方程并不是遍历的初始点,所以我们采取的是由后往前推的方式;别误会,遍历的s依然是从0到(1<<N)-1,只不过s状态的值是等到遍历到s时才确定的,而不是以往的做法s在遍历到s‘时就已经被赋值了;如果不明白,可以看下代码;

 Java代码

import java.util.Arrays;
import java.util.Scanner;

public class Main {

	// 用来存储两两之间的距离的哈
	private int[][] dis;
	private int[][] dp;
	
	private final static int INF = Integer.MAX_VALUE >> 1;
	private int n_len;
	public final static int N = 10;

	private int judg;

	public Main() {
		dis = new int[N + 1][N + 1];

		dp = new int[1 << (N + 1)][N + 1];
	}

	private void initial(int n_len) {
		// TODO Auto-generated method stub

		this.n_len = n_len;

		for (int i = 0; i < (1 << (n_len + 1)); i++) {
			Arrays.fill(dp[i], INF);
		}
		
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner cin = new Scanner(System.in);
		int n;
		Main ma = new Main();

		n = cin.nextInt();

		while (n != 0) {
			ma.initial(n);
			// 包含初始位置的哈,所以是N+1
			for (int i = 0; i < n + 1; i++) {
				for (int j = 0; j < n + 1; j++) {
					ma.dis[i][j] = cin.nextInt();
				}
			}

			System.out.println(ma.getShortestPath());

			n = cin.nextInt();
		}
	}

	private int getShortestPath() {
		// TODO Auto-generated method stub
		// floyd算法
		for (int k = 0; k < n_len + 1; k++) {
			for (int i = 0; i < n_len + 1; i++) {
				for (int j = 0; j < n_len + 1; j++) {

					if (dis[i][j] > dis[i][k] + dis[k][j]) {
						dis[i][j] = dis[i][k] + dis[k][j];				
					}
				}
			}
		}
		
		//先求边界条件
		for(int i=1;i< n_len + 1;i++){
			dp[1<<(i-1)][i] = dis[0][i];
		}
		/*
		 * 可以看到这个边界条件是并不能在遍历中作为初始状态,什么意思呢?就是说遍历的时候并不是从这些初始状态出发的;
		 * 那怎么办呢?可以利用从后往前推的方法;
		 * 比如:一般的情况是这样已知s'求出:dp[s][1] =  min(dp[s][1],dp[s'][2]+dis[2][1]);代表在s'的状态
		 * 以2为结尾的情况增加节点1的状态转移式子;
		 * 现在更改为已知s,dp[s][1] = min(dp[s][1],dp[s'][2]+dis[2][1]),
		 * s'与s相比在于s'不包含1节点;
		 */
		
		//遍历所有的状态;
		for(int s=1;s<(1<<(n_len));s++){
			//遍历该状态以什么结尾的;
			for(int j=1;j<n_len+1;j++){
				if((s & (1<<(j-1))) == 0){
					//说明s状态不包含j,上边两个因为s,j代表在s状态下j结尾的值
					continue;
				}
				for(int k=1;k < n_len+1;k++){
					if((s & (1<<(k-1))) != 0 && j!=k){
						//说明s状态包含了k,且j并不等于k;
						//下边代表的是s状态是从s'且以k为结尾转化过来的额;
						dp[s][j] =  Math.min(dp[s][j],dp[s^(1<<(j-1))][k]+dis[k][j]);
					}
				}
			}
		}
		int ans = INF;
		for(int i=1;i<n_len+1;i++){
			ans = Math.min(ans, dp[(1<<n_len)-1][i]+dis[i][0]);
		}
		return ans;
	}
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值