題目描述 Description |
---|
給你一個N個點M條邊的無向帶權圖。起點是任意的,要求每個點最多走兩次,把所有的點遍歷完一遍,費用最小是多少,注意有重邊(被坑到)。 |
輸入描述 Input Description |
有多組輸入數據,第一行給出一個N(1<=N<=10)和一個M分別表示需要拜訪的城市數和邊數。接下來M行,每一行給出a,b,c三個數表示a與b之間有一條花費c的路。輸入一直讀到EOF爲止 |
輸出描述 Output Description |
對於每個樣例輸出一個整數表示最小花費,如果沒有辦法遍歷所有城市則輸出-1. |
水題分析 Waterproblem Analysis |
首先觀察題目數據範圍,N只有10個,要求每個城市至少拜訪一遍,至多去兩次,那麼就可以用一個N位的3進制數代表當前的情況,每一位0,1,2三個數來表示該城市拜訪的次數,這種全部拜訪一遍的題經常採用這種方法儲存狀態要歸納總結。節省空間,用一個int就可以。同時我們可以預處理出每個數字代表的狀態來節省時間。當然這個題還要記錄到達這個這個狀態時在哪一個城市,否則會過度減枝,舉個例子。很明顯按1->2->3的順序遍歷和2->1->3的順序來遍歷得到的答案是不同的所以要再記錄當前遍歷到的點是哪個。題目中未說明起點是哪個點所以要分別以每一個點爲起點進行搜索。如果當前的答案已經超過算出的答案可以進行剪枝,同時要用鄰接矩陣保存圖只保留兩點之間的最短邊,否則會因爲入隊點數過多爆內存。 |
附上代碼:
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define ll long long
#define MAXM 60010
#define MAXN 50
#define INF 0x3f3f3f3f
//9223372036854775807
using namespace std;
int n,m,ans=INF;
int vis[MAXM][12];
int id[MAXN];
int g[MAXN][MAXN];
int state[MAXM][12];
struct node {
int u,dis,step;//當前點,當前花費,狀態
};
bool check(int x) {
for(int i=1;i<=n;++i) {
if(!state[x][i])return 0;
}
return 1;
}
void bfs(int x) {
queue<node> q;
q.push({x,0,id[x]});
vis[id[x]][x]=0;//起點入隊,並且記錄狀態
while(q.size()) {
node cur=q.front();
q.pop();
int u=cur.u;
int step=cur.step;
if(check(cur.step)) {
ans=min(ans,cur.dis);
continue;
}//檢查是否滿足每個城市都至少拜訪的了一次 ,如果成立就更新答案。
for(int v=1;v<=n;++v) {
if(v==u||g[u][v]==-1)continue;
if(state[cur.step][v]<2) {//一個點不能經過超過兩次
int tmp=step+id[v];
int dis=cur.dis+g[u][v];
if(dis>=ans)continue;//如果當前的花費已經比答案要多可以直接減枝了
if(dis<vis[tmp][v]) {//如果當前狀態來過那麼只有比之前少的花費才繼續往下搜索
q.push({v,dis,tmp});
vis[tmp][v]=dis;
}
}
}
}
}
void init() {
for(int i=1;i<=10;++i) id[i]=pow(3,i-1);//預處理出每一個點拜訪一次對狀態數的影響
int tot=pow(3,10);
for(int i=1;i<=tot;++i) {
int tmp=i,p=0;
while(tmp) {
state[i][++p]=tmp%3;//代表i狀態第p個城市拜訪了多少次
tmp/=3;
}
}//預處理出每一個狀態各個城市的拜訪情況
}
int main() {
init();
while(scanf("%d%d",&n,&m)!=EOF) {
memset(vis,INF,sizeof(vis));//清空邊
memset(g,-1,sizeof(g));//清空圖
for(int i=1;i<=m;++i) {
int u,v,val;
scanf("%d%d%d",&u,&v,&val);
if(g[u][v]==-1) {
g[u][v]=g[v][u]=val;
}
else {
g[u][v]=min(g[u][v],val);//每次只保留兩點之間最短的路徑
//如果用鏈式前向星會在搜索時,向隊列中加入過多的無用狀態
g[v][u]=g[u][v];
}
}
ans=INF;
for(int i=1;i<=n;++i) {
bfs(i);//以每一個點爲起點進行搜索
}
if(ans==INF) printf("-1\n");
else printf("%d\n",ans);
}
}
//最近狀態真是不好,一個水題DEBUG一天。靜下心來,慢慢思考吧。 I DON'T CARE