標籤:
題意即求一個最小頂點覆蓋。
對於沒有孤立點的圖G=(V,E),最大獨立集+最小頂點覆蓋= V。(往最大獨立集加點)
問題可以變成求樹上的最大獨立集合。
每個結點的選擇和其父節點選不選有關,
dp(u,1)表示父節點選,這時u不可選,
dp(u,0)表示父節點不選,這時u可選可不選。
#include<bits/stdc++.h>using namespace std;const int maxn = 1501;int meo[maxn][2];int vis[maxn][2], clk;int hd[maxn],nx[maxn<<1],to[maxn<<1],ec;void add(int u,int v){ to[ec] = v; nx[ec] = hd[u]; hd[u] = ec++;}int dp(int u,int a = 0,int f = -1)//a表示父節點選不選{ if(vis[u][a] == clk) return meo[u][a]; vis[u][a] = clk; int &re = meo[u][a]; re = 0; int pick = 1; for(int i = hd[u]; ~i; i = nx[i]){ int v = to[i]; if(v == f) continue; if(!a){ pick += dp(v,1,u); } re += dp(v,0,u); } if(!a) re = max(re,pick); return re;}//#define LOCALint main(){#ifdef LOCAL freopen("in.txt","r",stdin);#endif int n; while(~scanf("%d",&n)){ memset(hd,-1,sizeof(hd)); ec = 0; for(int i = 0; i < n; i++){ int u,sn; scanf("%d:(%d)",&u,&sn); while(sn--){ int v;scanf("%d",&v); add(u,v); add(v,u); } } clk++; printf("%d\n",n-dp(0)); } return 0;}
最小點覆蓋還可以用二分匹配來做
關鍵代碼,下面可以適合無向圖,如果用有向圖的演算法一個匹配會算兩次。
int link[maxn];int vis[maxn], clk;bool aug(int u){ if(vis[u] == clk) return false; vis[u] = clk; for(int i = hd[u]; ~i; i = nx[i]){ int v = to[i]; if(!~link[v] || aug(link[v])){ link[v] = u; link[u] = v; return true; } } return false;}int Hungary(int n){ memset(link,-1,sizeof(link)); int ans = 0; for(int i = 0; i < n; i++){ if(link[i]<0){ clk++; if(aug(i)) ans++; } } return ans;}
也可以直接dp求最小點覆蓋集合,
f[u][p]表示以u為根的樹最小點覆蓋,p表示選不選u。
當選u的時候,子結點可選可不選,
當不選u的時候,子結點都選。
(實際上存在在某些結點只選一個子節點的最優解的情況,但是這樣做並不會丟解)
int f[maxn][2],vis[maxn],clk;void dfs(int u = 0,int fa = -1){ vis[u] = clk; f[u][0] = 0; f[u][1] = 1; for(int i = hd[u]; ~i; i = nx[i]){ int v = to[i]; if(v == fa) continue; if(vis[v] != clk) dfs(v,u); f[u][1] += min(f[v][0],f[v][1]); f[u][0] += f[v][1]; }}
LA 2038 Strategic game