旅行商问题(Traveling Saleman Problem,TSP)又译为旅行推销员问题、货郎担问题,简称为TSP问题,是最基本的路线问题,该问题是在寻求单一旅行者由起点出发,通过所有给定的需求点之后,最后再回到原点的最小路径成本。
http://acm.hdu.edu.cn/showproblem.php?pid=4568
进出一次,找到所有能找到的宝藏,裸的TSP问题。将整个边界理解成一个点,找到每两个宝藏之间的最短距离和每个宝藏的离边界点的最短距离。
先用spfa预处理出宝藏与宝藏之间的最短距离,宝藏到边界的最短距离,然后就是经典的求TSP过程了。
const int Max_N = 208 ; const int Max_K = 15 ; const int inf = (1<<30) ; struct Node{ int x ; int y ; int step ; friend bool operator < (const Node &A , const Node &B){ return A.step > B.step ; } Node(){} Node(int i , int j , int k):x(i) , y(j) ,step(k){} }; struct BaoZhang{ int x ; int y ; }; BaoZhang Bao[Max_K] ; int Bao_Id[Max_N][Max_N] ; int N , M , K ; int money[Max_N][Max_N] ; bool visited[Max_N][Max_N] ; int d[4][2] = { {1,0} ,{-1 ,0} ,{0 ,-1} ,{0 ,1} } ; int dist[Max_K][Max_K] ; int dp[1<<Max_K][Max_K] ; int cango(int x , int y){ return 1 <= x && x <= N && 1 <= y && y <= M ; } void bfs(BaoZhang b , int u){ priority_queue<Node> que ; memset(visited , 0 , sizeof(visited)) ; que.push(Node(b.x , b.y , 0)) ; visited[b.x][b.y] = 1 ; int OK_dot = 0 ; while(! que.empty()){ Node now = que.top() ; que.pop() ; if(Bao_Id[now.x][now.y] > 0){ int v = Bao_Id[now.x][now.y] ; dist[u][v] = now.step ; OK_dot++ ; } if(dist[u][0] == inf && (now.x == 1 || now.x == N || now.y == 1 || now.y == M) ){ dist[u][0] = now.step ; dist[0][u] = now.step + money[b.x][b.y] ; OK_dot++ ; } if(OK_dot == K+1) return ; for(int i = 0 ; i < 4 ; i++){ int x = now.x + d[i][0] ; int y = now.y + d[i][1] ; if(cango(x,y) && money[x][y] != -1 && !visited[x][y]){ visited[x][y] = 1 ; que.push(Node(x,y,now.step+money[x][y])) ; } } } } int TSP(int n){ int i , j , k , limit ; dist[0][0] = 0 ; limit = 1<<(n+1) ; for(i = 0 ; i < limit ; i++) for(j = 0 ; j <= n ; j++) dp[i][j] = inf ; for(i = 0 ; i <= n ; i++) dp[1<<i][i] = dist[0][i] ; for(i = 0 ; i < limit ; i++){ for(j = 0 ; j <= n ; j++){ if(i & (1<<j) == 0) continue ; for(k = 0 ; k <= n ; k++){ if(i & (1<<k) == 0) continue ; if(dist[k][j] == inf) continue ; dp[i][j] = min(dp[i][j], dp[i^(1<<j)][k] + dist[k][j]) ; } } } return dp[limit-1][0] ; } int main(){ int T , i , j , k; scanf("%d" ,&T) ; while(T--){ scanf("%d%d" ,&N ,&M) ; for(i = 1 ; i <= N ; i++) for(j = 1 ; j <= M ; j++) scanf("%d" ,&money[i][j]) ; memset(Bao_Id , -1 , sizeof(Bao_Id)) ; scanf("%d" ,&K) ; for(i = 1 ; i <= K ; i++){ scanf("%d%d" ,&Bao[i].x ,&Bao[i].y) ; Bao[i].x++ ; Bao[i].y++ ; Bao_Id[Bao[i].x][Bao[i].y] = i ; } if(!K){ puts("0") ; continue ; } for(i = 0 ; i <= K ; i++) for(j = 0 ; j <= K ; j++) dist[i][j] = inf ; for(i = 1 ; i <= K ; i++) bfs(Bao[i] , i) ; int ans = TSP(K) ; if(ans == inf) puts("0") ; else printf("%d\n" ,ans) ; } return 0 ; }
http://poj.org/problem?id=3311
Floyd + TSP
题意是有N个城市(1~N)和一个PIZZA店(0),要求一条回路,从0出发,又回到0,而且距离最短。
const int Max_N = 12 ; const int inf = (1<<30) ; int dist[Max_N][Max_N] ; int N ; int dp[1<<Max_N][Max_N] ; void Floyd(){ int i , j , k ; for(i = 0 ; i <= N ; i++){ for(j = 0 ; j <= N ; j++){ for(k = 0 ; k <= N ; k++){ if(dist[i][j] > dist[i][k] + dist[k][j]) dist[i][j] = dist[i][k] + dist[k][j] ; } } } } int DP(){ int i , j , k ; int limit = 1<<(N+1) ; for(i = 0 ; i < limit ; i++){ for(j = 0 ; j <= N ; j++) dp[i][j] = inf ; } for(i = 0 ; i <= N ; i++) dp[1<<i][i] = dist[0][i] ; for(i = 0 ; i < limit ; i++){ for(j = 0 ; j <= N ; j++){ if(i & (1<<j) == 0) continue ; for(k = 0 ; k <= N ; k++){ if(i & (1<<k) == 0) continue ; if(dist[k][j] == inf) continue ; dp[i][j] = min(dp[i][j] , dp[i^(1<<j)][k] + dist[k][j]) ; } } } return dp[limit-1][0] ; } int main(){ int i , j ; while(cin>>N && N){ for(i = 0 ; i <= N ; i++){ for(j = 0 ; j <= N ; j++) scanf("%d" ,&dist[i][j]) ; } Floyd() ; printf("%d\n" , DP()) ; } return 0 ; }
http://acm.hdu.edu.cn/showproblem.php?pid=4284
题目:给出一些城市,从1出发,旅游一圈回到1,由于花费可能不够,所以选择一些城市打工,打工之前需要花费d买一个证,工资为c。选中的城市必须去工作一次,而且只能工作一次,问能不能完成旅行.
1:重边:只要取最小的距离就可以 2:从u->v必须要有足够的钱 3:选中的城市必须工作 4:工作前必须先买证才能工作,钱不够不能工作(既不能合并工资和买证而得到受益)
const int Max_N = 108 ; const int Max_H = 16 ; const int inf = (1<<29) ; int money ; int N ; int dist[Max_N][Max_N] ; int dp[1<<Max_H][Max_H] ; struct Node{ int id ; int lisence ; int makemony ; void read(){ scanf("%d%d%d" ,&id , &makemony , &lisence) ; } }h[Max_H]; int H ; void Floyd(){ /*Floyd最精确模板*/ int i,j,k; for(k = 1; k <= N ; k++){ for(i = 1; i <= N ; i++){ if(dist[i][k] == inf) continue ; for(j = 1 ; j <= N ; j++){ if(dist[k][j] == inf) continue ; dist[i][j] = min(dist[i][j] , dist[i][k] + dist[k][j]); } } } } int DP(){ int i , j , k , u , v ; int limit = (1<<H) ; memset(dp , -1 , sizeof(dp)) ; for(i = 0 ; i < H ; i++){ v = h[i].id ; if(money >= dist[1][v] + h[i].lisence) dp[1<<i][i] = money - dist[1][v] - h[i].lisence + h[i].makemony ; } for(i = 1 ; i < limit ; i++){ for(j = 0 ; j < H ; j++){ if(dp[i][j] == -1) continue ; if(i & (1<<j) ==0) continue ; u = h[j].id ; for(k =0 ; k < H ; k++){ if(i & (1<<k)) continue ; v = h[k].id ; if(dp[i][j] >= dist[u][v] + h[k].lisence){ int now = (i ^ (1<<k)) ; dp[now][k] = max(dp[now][k] , dp[i][j] - dist[u][v] - h[k].lisence + h[k].makemony) ; } } } } for(i = 0 ; i < H ; i++){ if(dp[limit-1][i] >= dist[h[i].id][1]) return 1 ; } return 0 ; } int main(){ int T , m , i , j , u , v , w; scanf("%d" ,&T) ; while(T--){ scanf("%d%d%d" ,&N ,&m ,&money) ; for(i = 1 ; i <= N ; i++){ for(j = 1 ; j<= N ; j++) dist[i][j] = (i==j? 0 : inf) ; } while(m--){ scanf("%d%d%d" ,&u , &v ,&w) ; dist[u][v] = dist[v][u] = min(dist[u][v] , w) ; } cin>>H ; for(i = 0 ; i < H ; i++) h[i].read() ; Floyd() ; if(DP()) printf("YES\n") ; else printf("NO\n") ; } return 0 ; }
http://acm.fzu.edu.cn/problem.php?pid=2120
S得到了一个数,他认为相邻位上的数字与数字之间会产生不良影响,比如123,1和2之间产生一个不良影响值,2和3之间产生一个不良影响值。现在他想调整这个数每位的数字的顺序,使得最终得到的数的总的不良影响值最小,且没有前导0。
输入数据的第一行为T表示有T组数据。每组数据先输入一个整数n(0<n<1000000000),接下来输入10*10的矩阵,Aij表示数字i与数字j相邻产生的不良影响值,0<Aij<1000000,矩阵是对称的,Aij与Aji相等。
const int inf = 1<<29 ; string str ; int grid[10][10] ; int N ; int dp[1<<12][12] ; int dist[12][12] ; int DP(){ int i , j , k , limit , ans = inf ; limit = (1<<N) ; for(i = 0 ; i < limit ; i++){ for(j = 0 ; j < N ; j++) dp[i][j] = inf ; } for(i = 0 ; i < N ; i++){ if(str[i] == '0') continue ; dp[1<<i][i] = 0 ; } for(i = 0 ; i < limit ; i++){ for(j = 0 ; j < N ; j++){ if(i & (1<<j) == 0) continue ; for(k = 0 ; k < N ; k++){ if(i & (1<<k) == 0) continue ; dp[i][j] = min(dp[i][j] , dp[i^(1<<j)][k] + dist[k][j]) ; } } } for(i = 0 ; i < N ; i++){ ans = min(ans , dp[limit-1][i]) ; } return ans ; } int main(){ int T , i , j ; cin>>T ; while(T--){ cin>>str ; for(i = 0 ; i <= 9 ; i++){ for(j = 0 ; j <= 9 ; j++) scanf("%d" ,&grid[i][j]) ; } N = str.length() ; for(i = 0 ; i < N ; i++){ for(j = 0 ; j < N ; j++) dist[i][j] = grid[str[i]-'0'][str[j]-'0'] ; } printf("%d\n" ,DP()) ; } return 0 ; }