Harry And Dig Machine
题意:n*m的矩阵中有n(n<=10)个数字大于0的格子,从左上角即(0, 0)点开始,要求便利所有数字大于0的格子并最终返回(0, 0),每移动一步消耗一点体力,且只能上下左右移动,问最少消耗的体力;
思路:刚拿到这个题毫无头绪,搜了下题解,才知道这是一道TSP(旅行商)的经典题目;(以下来自百度百科)
旅行商问题(TravelingSalesmanProblem,TSP)是一个经典的组合优化问题。经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。从图论的角度来看,该问题实质是在一个带权完全无向图中,找一个权值最小的Hamilton回路。
用二进制的0,1表示是否走过某一点,进行状态压缩;
再看这道题,最多有11个点,,哎,不是10个吗,注意,起点也算是一个点!!!
相当于是11位的二进制,最大为(1<<12)-1;dp[i][j]表示为在i状态下最终到达j点消耗的最小体力,那么初始状态是由(0, 0)点(编号为1)到i点消耗dis(1, i)体力;最终状态是(1<<n)-1状态下最终到达1点(n表示共有n个点);
状态转移方程:dp[i][j]=min(dp[i][j], dp[i-(1<<(j-1))][k]+dis(j, k));
上式表示i状态下最终到达j点可以由先到达k点再到达j点转移过来,需要注意的事i状态中,必须已经有了j, k两点,即:i&(1<<(j-1))!=0 , 1&(1<<(k-1))!=0;
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
struct node{
int x, y;
node(){}
node(int x0, int y0){
x=x0, y=y0;
}
}point[15];
int dp[1<<12][15];
int dis(int i, int j){
return abs(point[i].x-point[j].x)+abs(point[i].y-point[j].y);
}
int main(){
int n, m;
while(~scanf("%d%d", &n, &m)){
int cnt=0;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
int x;
scanf("%d", &x);
if(i==0&&j==0){
point[++cnt]=node(i, j);
}
else if(x>0){
point[++cnt]=node(i, j);
}
}
}
if(cnt==1){//注意特殊情况的判断,只有一个点不需要移动;
printf("0\n");
continue;
}
memset(dp, INF, sizeof(dp));
for(int i=2; i<=cnt; i++){
dp[1<<(i-1)][i]=dis(1, i);
}
for(int i=1; i<(1<<cnt); i++){
for(int j=1; j<=cnt; j++){
if(i&(1<<(j-1))){
for(int k=1; k<=cnt; k++){
if(j==k) continue;
if(i&(1<<(k-1))){
dp[i][j]=min(dp[i][j], dp[i-(1<<(j-1))][k]+dis(k, j));
}
}
}
}
}
printf("%d\n", dp[(1<<cnt)-1][1]);
}
return 0;
}