题目描述
经历过与妻儿生离死别的牛牛,选择成为一名电工,过上平淡的日子。牛牛每天的工作是从家里出发,修理完小镇上所有坏掉的电线杆,然后回家和家人团聚。已知小镇是个n\times mn×m的区域,每块格子的类型有以下几种:

‘.’:表示该地点为空地。

‘#’:表示该地点正在施工,牛牛无法移动到该地点。

‘S’:表示牛牛的家。

‘T’:表示此处有一个坏掉的电线杆。

牛牛每秒可以往上/下/左/右四个方向移动一格,且牛牛修理一个电线杆所花的时间为tt秒,请问牛牛从家里出发修理完电线杆并最后回到家的最短时间是多少?
思路:因为电线杆的数量比较小,所以我们可以用状压来解决它。首先搜索求出电线杆和出发点和家的最短路。dp一维表示状态,第二维表示最后到达的点,然后递推公式就很简单了,枚举没走的所有电线杆,更新答案就行了。
代码:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int n,m,t,dir[4][2]={1,0,-1,0,0,1,0,-1};
char ch[220][220];
int id[220][220],dist[20][20],dist1[220][220];
struct Node{
    int x,y;
    Node(int xx,int yy):x(xx),y(yy){}
};
const int inf=0x3f3f3f3f;
void solve(int si,int sj,int p)
{
    //printf("%d\n",p);
    memset(dist1,inf,sizeof(dist1));
    dist1[si][sj]=0;
    queue<Node>q;
    q.push(Node(si,sj));
    while(!q.empty()){
        int x=q.front().x,y=q.front().y;
        q.pop();
        for(int i=0;i<4;i++){
            int xx=x+dir[i][0],yy=y+dir[i][1];
            if(xx<=n-1&&xx>=0&&yy<=m-1&&yy>=0){
                if(ch[xx][yy]=='#')continue;
                if(dist1[xx][yy]>dist1[x][y]+1){
                    dist1[xx][yy]=dist1[x][y]+1;
                    if(id[xx][yy])dist[p][id[xx][yy]]=dist1[xx][yy];
                    q.push(Node(xx,yy));
                }
            }
        }
    }
}
int dp[1<<17][20];
int main()
{
    scanf("%d%d%d",&n,&m,&t);
    for(int i=0;i<n;i++)
        scanf("%s",ch[i]);
    int st,cnt=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            //printf("%d\n",cnt);
            if(ch[i][j]=='S')
                id[i][j]=++cnt,st=cnt;//printf("%d\n",cnt);
            else if(ch[i][j]=='T')
                id[i][j]=++cnt;
        }
    }

    memset(dist,0x3f3f3f3f,sizeof(dist));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(ch[i][j]=='S'||ch[i][j]=='T'){
                solve(i,j,id[i][j]),dist[id[i][j]][id[i][j]]=0;
            }
        }
    }
    memset(dp,inf,sizeof(dp));
    dp[1<<(st-1)][st]=0;
    for(int i=1<<(st-1);i<1<<cnt;i++){
        for(int k=1;k<=cnt;k++){
            if((1<<(k-1)&i)==0)continue;
            for(int j=1;j<=cnt;j++){
                if(1<<(j-1)&i)continue;
                dp[i|(1<<(j-1))][j]=min(dp[i|(1<<(j-1))][j],dp[i][k]+dist[k][j]);
                //printf("%d %d %d %d %d %d\n",i|(1<<(j-1)),dp[i|(1<<(j-1))][j],i,k,dp[i][k],dist[k][j]);
            }
        }
    }
    int ans=0x3f3f3f3f;
    for(int i=1;i<=cnt;i++){
        ans=min(ans,dp[(1<<cnt)-1][i]+dist[i][st]);
    }
    if(ans==0x3f3f3f3f)printf("-1\n");
    else printf("%lld\n",(long long)(ans+(long long)t*(cnt-1)));
    return 0;
}