Pku 3140 Contestants Division (無向圖割邊+樹形DP)
由于該圖不是一顆樹,所以首先要將一些塊壓縮成點,所謂塊就是沒有割點的分量,于是只要求出割邊,然后刪除,之后對整個圖進行Floodfill,得到的連通分量再用割邊
補圖,得到的圖一定是一棵樹,然后就可以樹形DP了。
#include <iostream>
#define MAXN 100010
using namespace std;


struct list
{
list *next;
int ID;
};

int n, m;
int value[ MAXN ];

list data[ 1000000 ];
list *vec[ MAXN ];
list *Bri[ MAXN ];
int Ance[ MAXN ];
int Dep[ MAXN ];
int Color[ MAXN ];
int father[ MAXN ];
bool Cut[ MAXN ];
int num[ MAXN ];
#define G 0 // Grey 正在訪問
#define B 1 // Black 訪問完畢
#define W 2 // White 未曾訪問
int root, T;


/**//*******************求無向圖割邊割點********************
* list vec[i] 表示原的鄰接表 *
* list Bri[i] 保存割邊 *
* bool Cut[i] 保存割點 *
* int Ance[i] 保存和i或i的子孫鄰接的最高輩分的祖先的深度 *
* int Dep[i] 保存i點所在的深度 *
* int Color[i] 保存當前結點訪問情況 *
* int father[i] 保存i的父親結點的編號 *
* int num[i] 縮點后每個點所在的新塊的編號 *
*********************************************************/


int Min ( int a, int b )
{
return a < b ? a : b;
}


void BridgeCut( int key, int depth )
{

int son = 0;
Dep[ key ] = depth;
Color[ key ] = G;
Ance[ key ] = depth;

list *p, *q;


for( p = vec[ key ]->next; p ; p = p->next )
{

int next = p->ID;


if( next != father[ key ] && Color[ next ] == G)
{
Ance[ key ] = Min( Ance[ key ], Dep[ next ] );
}


if( Color[ next ] == W )
{

father[ next ] = key;
BridgeCut( next, depth + 1);
son ++; Ance[ key ] = Min( Ance[ key ], Ance[ next ] );
// 判割點

if( key == root )
{

if( son > 1 )
{
Cut[ key ] = true;
}

}else
{

if( Ance[ next ] >= Dep[ key ] )
{
Cut[ key ] = true;
}
}

// 判割邊

if( Ance[ next ] > Dep[ key ] )
{
q = &data[ T++ ];
q->ID = next;
q->next = Bri[ key ]->next;
Bri[ key ]->next = q;
q = &data[ T++ ];
q->ID = key;
q->next = Bri[ next ]->next;
Bri[ next ]->next = q;
}
}
}

Color[ key ] = B;
}



void CreateGraph()
{

int i, x, y;
T = 0;

for(i = 1; i <= n; i++)
scanf("%d", &value[i]);

for(i = 1; i <= n; i++)
{

Bri[i] = &data[ T++ ];
Bri[i]->ID = i;
Bri[i]->next = NULL;

vec[i] = &data[ T++ ];
vec[i]->ID = i;
vec[i]->next = NULL;

Ance[i] = INT_MAX;

Cut[i] = false;
Color[ i ] = W;
}

list *p;

while( m-- )
{
scanf("%d %d", &x, &y);

p = &data[ T++ ];
p->next = vec[x]->next;
p->ID = y;
vec[x]->next = p;

p = &data[ T++ ];
p->next = vec[y]->next;
p->ID = x;
vec[y]->next = p;
}
}


/**//*
割邊割點輸出,以及縮塊后的圖的鄰接表
*/


void Print()
{
list *p;
int i;

for(i = 1; i <= n; i++)
{
printf("%d: ", i);

for(p = Bri[i]->next; p; p = p->next)
{
printf("%d ", p->ID);
}
puts("");
}

for(i = 1; i <= n; i++)
{
if( Cut[i] )
printf("point : %d\n", i);
}
}




/**//*以下是樹形DP的過程*/



bool IsBridge(int u, int v)
{
list *p;

for( p = Bri[u]->next; p ; p = p->next )
{
if( p->ID == v )
return 1;
}
return 0;
}

int F;

// 分塊

void dfs( int key )
{
list *p;
num[ key ] = F;

for(p = vec[ key ]->next; p ; p = p->next )
{

if( Color[ p->ID ] == W && !IsBridge(key, p->ID) )
{
Color[ p->ID ] = B;
dfs( p->ID );
}
}
}




void FloodFill()
{
int i;
for(i = 1; i <= n; i++)
Color[i] = W;
F = 1;

for(i = 1; i <= n; i++)
{

if( Color[i] == W )
{
Color[i] = B;
dfs(i);
F ++;
}
}
}

__int64 AllVal[ MAXN ];
__int64 M, zong;
list *New[ MAXN ];
int hash[ MAXN ];


// TreeDp

void Search( int key )
{
list *p;
int son = 0;


for( p = New[ key ]->next; p; p = p->next)
{
int q = p->ID;
if( hash[ q ] )
continue;
son ++;
hash[ q ] = 1;
Search(q);
AllVal[ key ] += (__int64)AllVal[ q ];
__int64 a = zong - AllVal[ q ];
__int64 b = AllVal[ q ];
a -= b;
if( a < 0)
a = -a;
if( a < M )
M = a;
}
}



void TreeDp()
{

int i;
M = 0;
memset( AllVal, 0, sizeof(AllVal) );

for(i = 1; i <= n; i++)
{
AllVal[ num[i] ] += (__int64)value[i];
M += (__int64)value[i];
}
zong = M;
F --;

for(i = 1; i <= F; i++)
{
New[i] = &data[ T++ ];
New[i]->ID = i;
New[i]->next = NULL;
}

list *p, *q;


for(i = 1; i <= n; i++)
{

for(p = Bri[i]->next; p; p = p->next)
{
q = &data[ T++ ];
q->ID = num[ p->ID ];
q->next = New[ num[i] ]->next;
New[ num[i] ]->next = q;
}
}

memset( hash, 0, sizeof(hash) );
hash[1] = 1;
Search(1);
}


int main()
{

int i;
int t = 1;


while( scanf("%d %d", &n, &m) != EOF && (n || m) )
{
CreateGraph();
root = 1;
father[ root ] = 0;
BridgeCut( root, 0 );
FloodFill();
//Print();
TreeDp();
printf("Case %d: %I64d\n", t++, M);
}
return 0;
}

補圖,得到的圖一定是一棵樹,然后就可以樹形DP了。































































































































































































































































































































































posted on 2009-05-07 13:35 英雄哪里出來 閱讀(687) 評論(1) 編輯 收藏 引用 所屬分類: ACM