poj-3866 poj Exclusive Access 2

呂振麒 bio photo By 呂振麒 Comment

傳送門:

poj 3866 : Exclusive Access 2

題意:

如果是在POJ看懂敘述才來,您真是辛苦了!!
本題求:如何將無向圖化為有向無環圖(DAG),並且使得最長路徑儘量小。

思路:

如果只是要來看個提示的:
將路徑視為chain的話,這題使用dp維護minimal antichain cover,antichain是一群無任何路徑的點。


這邊我們忽略繁雜的輸入輸出問題,需要可以參考code(寫的也不甚好就是了)。

第一步: 先整理出所有 antichain ,使用DP。

枚舉所有點的選取狀態,對於每個狀態先抽出一點來檢查是否與其他點沒有連線,再檢查不包含此點的狀態來維護目前狀態。


for(int mask=1 ; mask<(1<<cnt) ; mask++){
  withedge[mask] = pre_cal(mask);
}

inline bool pre_cal(int mask){
  int lsb = 0;
  while((1<<lsb) & ~mask) ++lsb;
  for(int pos = lsb+1 ; (1<<pos) < mask ; ++pos)
    if(((1<<pos) & mask) && conn[lsb][pos] ) 
      return true;
  return withedge[mask ^ (1<<lsb)];
}

第二步:枚舉每個狀態的所有antichain子集合,找最優的來更新

子集合找法不知道大家知道嗎,下方code第二行釀,整體複雜度是3^N


for(int mask=1 ; mask<(1<<cnt) ; mask++){
  for(int submask=mask ; submask>0 ; submask = ((submask-1)&mask)){
    if(withedge[submask]) continue;	// 內部有邊就不是antichain
    if(dp[mask-submask] + 1 < dp[mask]){
      dp[mask] = dp[mask-submask] + 1;
      dprecord[mask] = submask;		// 要邊紀錄用哪的子集合更新的
    }
  }
}

最後最後,沿著剛剛記錄下來的子集合trace回去,邊幫各團分組,用力一拍就可以印出答案了!!

AC code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
int T,M,N,K,I,c,ans,cnt;
char to_char[20];
int to_int[200], to_grp[200];
bool conn[20][20], withedge[3000000];
int dp[50000], dprecord[50000];
string in[105];

inline bool pre_cal(int mask){
  int lsb = 0;
  while((1<<lsb) & ~mask) ++lsb;
  for(int pos = lsb+1 ; (1<<pos) < mask ; ++pos)
    if(((1<<pos) & mask) && conn[lsb][pos] ) 
      return true;
  return withedge[mask ^ (1<<lsb)];
}

void init(){
  string s1,s2;
  char a,b;
  memset(to_int, -1, sizeof(to_int));
  cin >> N;
  for(int i=0;i<N;i++){
    cin >> s1 >> s2;
    a = s1[0], b = s2[0];
    if(to_int[a] == -1){      // 沒出現過就開新組別
      to_int[a] = cnt;
      to_char[cnt++] = a;
    } 
    if(to_int[b] == -1){
      to_int[b] = cnt;
      to_char[cnt++] = b;
    }
    conn[to_int[a]][to_int[b]] = 1;     // 建圖
    conn[to_int[b]][to_int[a]] = 1;
    in[i] = a;        // 紀錄 input 
    in[i] += b;
  }
  fill(dp, dp+50000, 30);
  dp[0] = 0;
}
void sol(){
  for(int mask=1 ; mask<(1<<cnt) ; mask++){
    withedge[mask] = pre_cal(mask);
  }

  for(int mask=1 ; mask<(1<<cnt) ; mask++){
    for(int submask=mask ; submask>0 ; submask = ((submask-1)&mask)){
      if(withedge[submask]) continue;
      if(dp[mask-submask] + 1 < dp[mask]){
        dp[mask] = dp[mask-submask] + 1;
        dprecord[mask] = submask;
      }
    }
  }
  
  //    沿著trace回去 順便重新分組
  for(int mask=(1<<cnt)-1, grp=0 ; mask ; ++grp,mask -= dprecord[mask]){
    int sbmsk = dprecord[mask] ; 
    for(int i=0 ; sbmsk>>i ; i++)if((sbmsk>>i) & 1)
      to_grp[i] = grp;
  }
  
  //    用力拍出答案!
  cout << dp[(1<<cnt)-1]-2 << endl;
#define group(x) (to_grp[to_int[in[i][x]]])
  for(int i=0;i<N;i++){
    if( group(0) > group(1)) swap(in[i][0], in[i][1]);
    cout << in[i][0] << " " << in[i][1] << endl;
  }
}

int main(){
  init();
  sol();
}
comments powered by Disqus