1. 程式人生 > >uva658(最短路徑+隱式圖+狀態壓縮)

uva658(最短路徑+隱式圖+狀態壓縮)

匹配 define top har 讀者 還記得 題意 priority 節點

題目連接(vj):https://vjudge.net/problem/UVA-658

題意:補丁在修正 bug 時,有時也會引入新的 bug。假定有 n(n≤20)個潛在 bug 和 m(m≤100) 個補丁,每個補丁用兩個長度為 n 的字符串表示,其中字符串的每個位置表示一個 bug。第一 個串表示打補丁之前的狀態(“-” 表示該 bug 必須不存在,“+” 表示必須存在,0 表示無所 謂),第二個串表示打補丁之後的狀態(“-” 表示不存在,“+” 表示存在,0 表示不變)。每 個補丁都有一個執行時間,你的任務是用最少的時間把一個所有 bug 都存在的軟件通過打補 丁的方式變得沒有 bug。一個補丁可以打多次。 在任意時刻,每個 bug 可能存在也可能不存在,所以可以用一個 n 位二進制串表示當前軟 件的 “狀態”。打完補丁之後,bug 狀態會發生改變,對應 “狀態轉移”。是不是很像動態規 劃?可惜動態規劃是行不通的,因為狀態經過多次轉移之後可能會回到以前的狀態,即狀態 圖並不是 DAG。如果直接用記憶化搜索,會出現無限遞歸。 正確的方法是把狀態看成結點,狀態轉移看成邊,轉化成圖論中的最短路徑問題,然後 使用 Dijkstra 或 Bellman-Ford 算法求解。不過這道題和普通的最短路徑問題不一樣:結點很 多,多達 2 n 個,而且很多狀態根本遇不到(即不管怎麽打補丁,也不可能打成那個狀態), 所以沒有必要像前面那樣先把圖儲存好。 還記得第 7 章中介紹的 “隱式圖搜索” 嗎?這裏也可以用相同的方法:當需要得到某個結 點 u 出發的所有邊時,不是去讀 G[u],而是直接枚舉所有 m 個補丁,看看是否能打得上。不管 是 Dijsktra 算法還是 Bellman-Ford 算法,這個方法都適用。本題很經典,強烈建議讀者編程實現。

以上題意以及思路來自紫書

解題思路:抽象成最短路問題,因為狀態(節點)太多,而且很多不會出現,所以不能單獨建完圖再跑最短路,所以采用隱式圖,每次擴展遍歷一遍已知補丁,匹配成功則看是否可以松弛,記錄松弛後的狀態。

當前狀態唯一確定,所以壓縮為一個n位二進制串,0表示無bug,1表示有,而補丁的狀態不唯一確定,所以用兩個二進制串表示,has串和no串,has串1表示該位置一定有,no串1表示該位置一定無。

匹配方法:cur表示當前狀態。如果 cur&no==0並且cur|yes=cur 則匹配成功,匹配成功後松弛後的狀態為 v=(u|p.to_has)&(~p.to_no)。然後跑dijkstra即可。

代碼如下:

#include<bits/stdc++.h>
#define MAX (1<<20)+10
#define INF 0x3fffffff
using namespace std;

struct data
{
    int from_no,from_has,to_no,to_has,dist;
} patch[105];

struct heapnode
{
    int d,u;
    bool operator < (const heapnode& rhs) const
    {
        return d>rhs.d;
    }
};

int l,m; bool vis[MAX]; long long d[MAX]; void init(void) { for(int i=0; i<m; i++) { patch[i].from_has=0; patch[i].from_no=0; patch[i].to_has=0; patch[i].to_no=0; } memset(vis,0,sizeof(vis)); for(int i=0; i<MAX; i++) d[i]=INF; } bool dijkstra(void) { priority_queue<heapnode> Q; int s=(1<<l)-1; d[s]=0;; Q.push((heapnode){0,s}); while(!Q.empty()) { heapnode x=Q.top(); Q.pop(); int u=x.u; if(vis[u]) continue; vis[u]=1; for(int i=0; i<m; i++) { data &p=patch[i]; if(!(u&p.from_no)&&((u|p.from_has)==u))//匹配成功 { int v=(u|p.to_has)&(~p.to_no); if(d[v]>d[u]+p.dist) { d[v]=d[u]+p.dist; Q.push((heapnode){d[v],v}); } } } } if(d[0]==INF) return 0; return 1; } int main() { int T=0; while(~scanf("%d%d",&l,&m)&&l) { init(); for(int i=0; i<m; i++) { char from[25],to[25]; int dis; scanf("%d%s%s",&dis,from,to); patch[i].dist=dis; for(int j=0; j<l; j++) { if(from[j]==+) patch[i].from_has=patch[i].from_has|(1<<j); if(from[j]==-) patch[i].from_no=patch[i].from_no|(1<<j); if(to[j]==+) patch[i].to_has=patch[i].to_has|(1<<j); if(to[j]==-) patch[i].to_no=patch[i].to_no|(1<<j); } } printf("Product %d\n",++T); if(dijkstra()) printf("Fastest sequence takes %d seconds.\n",d[0]); else printf("Bugs cannot be fixed.\n"); printf("\n"); } }

uva658(最短路徑+隱式圖+狀態壓縮)