旅行商问题(Traveling Saleman Problem,TSP)又译为旅行推销员问题、货郎担问题,简称为TSP问题,是最基本的路线问题,该问题是在寻求单一旅行者由起点出发,通过所有给定的需求点之后,最后再回到原点的最小路径成本。
HDU 4568
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 ;
}
POJ 3311
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 ;
}
HDU 4248
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 ;
}
FZU 2120
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 ;
}