今天第一次系统的学习了一下最短路算法,开始刷第十一章,第一次写Dijkstra算法,出现了很多喜闻乐见的错误。。而且uva上样例很水,瓢虫也很水 ,坑了我好久。
首先是对于结点的处理,我们必须要维护一个二元组,一个表示结点一个表示当前结点最短路。 因为Dijkstra算法利用了优先队列来加速算法,所以需要定义小于运算符,一开始我直接将状态装进了优先队列,显然是不对的,因为优先队列的作用就是取出当前距离最短的结点。
其次,说说最短路算法蕴含的巧妙思想: 每次从当前所有还未标记的结点中选择一个距离最小的点,从这个点更新与之相连的所有结点 。重复此过程 。
为什么这样做是正确的呢? 百度百科上有一个动态图可以帮助我们很好的理解这个过程 。传送门:点击打开链接
该题是一个很巧妙的最短路问题,需要我们把模型抽象出来,看清楚要解决的问题的实质是什么。
该题是复杂状态的最短路问题,需要将状态抽象出来当做结点 。
将状态集合当做结点,将所花费的时间当做边的权值,两个“结点”是否相连取决于第一个字符串与该结点的关系! 真是好题 ~
另外一点,用动态规划进行状态转移时很重要的一点是不能转移到以前的状态,即状态图不是DAG,所以不能用记忆化搜索 。
细节参见代码:
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 22;
const int maxm = 105;
int n,m,kase = 0,ok = 0 ,d[1<<maxn],done[1<<maxn];
struct node{
int t;
char a[maxn],b[maxn];
}pat[maxm];
struct Node{
int bugs,dist;
bool operator < (const Node& v) const {
return dist > v.dist;
}
};
int dijkstra() {
priority_queue<Node> q;
for(int i=0;i<(1<<n);i++) { done[i] = 0; d[i] = INF; }
Node u; u.dist = 0; u.bugs = (1<<n)-1;
d[u.bugs] = 0;
q.push(u);
while(!q.empty()) {
Node u = q.top(); q.pop();
if(u.bugs == 0) return u.dist;
if(done[u.bugs]) continue;
done[u.bugs] = true;
for(int i=1;i<=m;i++) {
bool ok = true;
for(int j=0;j<n;j++) { //检查该结点是否可以连一条边
if(pat[i].a[j] == '-' && u.bugs & (1<<j)) { ok = false; break; }
if(pat[i].a[j] == '+' && !(u.bugs & (1<<j))) { ok = false; break; }
}
if(ok) {
Node v = u ;
for(int j=0;j<n;j++) { //更新找到下一个结点
if(pat[i].b[j] == '-') {
v.bugs &= ~(1<<j);
}
else if(pat[i].b[j] == '+') {
v.bugs |= (1<<j);
}
}
if(d[v.bugs] > u.dist + pat[i].t) {
d[v.bugs] = u.dist + pat[i].t;
v.dist = d[v.bugs];
q.push(v);
}
}
}
}
return -1;
}
int main() {
while(~scanf("%d%d",&n,&m)) {
if( !n && !m ) return 0;
for(int i=1;i<=m;i++) {
scanf("%d%s%s",&pat[i].t,pat[i].a,pat[i].b);
}
int ans = dijkstra();
printf("Product %d\n",++kase);
if(ans < 0) printf("Bugs cannot be fixed.\n\n");
else printf("Fastest sequence takes %d seconds.\n\n",ans);
}
return 0;
}