在一般的图中应用Dancing Link边表的一个实例

之前本沙茶成功地在网络流图中搞出Dancing Link边表,那么对于一般的图,是否也能用Dancing Link边表呢?答案是肯定的。

边类型(带权的,不带边权的图把len域去掉即可):
struct  edge {
    
int  a, b, len, pre, next;
} ed[MAXM];
初始化表头:
void  init_d()
{
    re(i, n) ed[i].a 
=  ed[i].pre  =  ed[i].next  =  i;
    
if  (n  %   2 ) m  =  n  +   1 else  m  =  n;
}
加边(这里是加有向边,如果加无向边的话,再加一条边<b, a, len>即可):
void  add_edge( int  a,  int  b,  int  len)
{
    ed[m].a 
=  a; ed[m].b  =  b; ed[m].len  =  len; ed[m].pre  =  ed[a].pre; ed[m].next  =  a; ed[a].pre  =  m; ed[ed[m].pre].next  =  m ++ ;
}
在一般的图中应用Dancing Link边表的优势:【1】能够有效处理删边删点问题;【2】在无向图中,能够很快找到一条边对应的逆向边;【3】最大的优势是:如果要从某一条单点链表(其实整个边表可以看成N个单点链表的并,N为图中的点数)的中间开始遍历,或者逆向遍历整个表的话,一般的邻接链表几乎不可能完成,一般的边表也很麻烦,这种Dancing Link边表则可以很快搞定。不过它也有缺点:空间消耗比邻接链表和一般边表大一些(不过这个影响不大)。

【应用实例】 PKU1062(PKU上少有的中文题)
很水的最短路问题。将每个物品当成一个点,若j可作为i的优惠品则连边<i, j>,边权为优惠价格,然后,枚举等级限制(由于物品1是必须选的,因此设最大等级限制为lmt,物品1的等级为V,则可在[V-lmt, V]范围内枚举最低等级,最高等级就是(最低等级+lmt)),将所有不在等级限制内的点全部删除(其实不用真删除,只要设一个del数组避开它们即可),求从结点1到各结点的最短路,则min(dist[i]+cs[i])(dist[i]表示1到i的最短路,cs[i]表示直接购买物品i的价格)就是结果。
代码(2Y,一开始把solve里的g[j]弄成g[i]了囧……静态查错V5啊……神犇不要鄙视):
#include  < iostream >
#include 
< stdio.h >
#include 
< queue >
#include 
< utility >
using   namespace  std;
#define  re(i, n) for (int i=0; i<n; i++)
#define  re3(i, l, r) for (int i=l; i<=r; i++)
typedef pair 
< int int >  i_i;
typedef priority_queue 
< i_i, vector < i_i > , greater < i_i >   >  pqi_i;
const   int  MAXN  =   100 , MAXM  =   30000 , INF  =   ~ 0U   >>   2 ;
struct  edge {
    
int  a, b, len, pre, next;
} ed[MAXM];
int  n, m, s, lmt, cs[MAXN], g[MAXN], dist[MAXN], res  =  INF;
bool  del[MAXN];
pqi_i q;
void  init_d()
{
    re(i, n) ed[i].a 
=  ed[i].pre  =  ed[i].next  =  i;
    
if  (n  %   2 ) m  =  n  +   1 else  m  =  n;
}
void  add_edge( int  a,  int  b,  int  len)
{
    ed[m].a 
=  a; ed[m].b  =  b; ed[m].len  =  len; ed[m].pre  =  ed[a].pre; ed[m].next  =  a; ed[a].pre  =  m; ed[ed[m].pre].next  =  m ++ ;
}
void  init()
{
    
int  b0, x, y;
    scanf(
" %d%d " & lmt,  & n);
    init_d();
    re(i, n) {
        scanf(
" %d%d%d " & cs[i],  & g[i],  & x);
        re(j, x) {
            scanf(
" %d%d " & b0,  & y);
            add_edge(i, 
-- b0, y);
        }
    }
}
void  sol1()
{
    re(i, n) 
if  ( ! del[i]) dist[i]  =  INF  +   1 ; q.push(i_i( 0 , s));
    
int  i, j, d0, d1;
    
while  ( ! q.empty()) {
        d0 
=  q.top().first; i  =  q.top().second; q.pop();
        
if  (dist[i]  ==  INF  +   1 ) {
            dist[i] 
=  d0;
            
for  ( int  p = ed[i].next; p  !=  i; p = ed[p].next) {
                j 
=  ed[p].b;
                
if  ( ! del[j]) {
                    d1 
=  d0  +  ed[p].len; q.push(i_i(d1, j));
                }
            }
        }
    }
    re(i, n) 
if  ( ! del[i]) {
        d0 
=  cs[i]  +  dist[i];
        
if  (d0  <  res) res  =  d0;
    }
}
void  solve()
{
    
int  lf, rt; s  =   0 ;
    re3(i, 
0 , lmt) {
        lf 
=  g[s]  -  i; rt  =  lf  +  lmt;
        re(j, n) del[j] 
=  g[j]  <  lf  ||  g[j]  >  rt;
        sol1();
    }
}
void  pri()
{
    printf(
" %d\n " , res);
}
int  main()
{
    init();
    solve();
    pri();
    
return   0 ;
}

你可能感兴趣的:(在一般的图中应用Dancing Link边表的一个实例)