Luogu P3577 [POI2014]TUR-Tourism

Luogu P3577 [POI2014]TUR-Tourism

題目連接c++

題目大意:給出一張\(n\)個點,\(m\)條邊的無向圖,保證任意兩點之間沒有點數超過\(10\)的簡單路徑。選擇第\(i\)個點要支付\(C_i\)的代價,要求用最小的代價使得每一個點被選擇或者與一個被選擇的點相鄰。spa

\(n\leq 20000,m\leq 25000,C_i\leq 1000\)code


能夠發現,若是原問題是在樹上的話就很簡單。因此咱們先建出\(dfs\)樹。由於這是無向圖,因此全部的非樹邊都是返祖邊。由題目中那個神奇的性質,咱們能夠知道,這棵樹的深度不超過\(10\)繼承

因而咱們能夠狀壓。設\(f_{i,S}\)表示第\(i\)個點,其祖先的狀態爲\(S\)的最小代價。遞歸

狀壓的狀態有三種:get

​ 0:沒有選擇且沒被覆蓋it

​ 1.沒有選擇但被覆蓋了class

​ 2.選擇了支付

轉移就有點厲害,聽說是泛化揹包poi

當由\(fa_v\)遞歸到\(v\)的時候,\(dfs\)序比\(v\)小的點中除了\(v\)到根這條鏈上都已經被覆蓋了。而後將\(f_{fa_v,S}\)\(DP\)值繼承給\(f_{v,S'}\)。繼承的時候討論是否選擇\(v\)獲得\(S'\)

一直遞歸到底後回溯,\(f_{fa_v,S}=\min\{f_{v,S+3^{dep_v}},f_{v,S+2*3^{dep_v}}\}\)。接着遞歸下一個兒子。這個\(DP\)是按\(dfs\)序來的,當一個點出棧了事後,保證這個點已經被覆蓋,無需再考慮。

\(DP\)的結果就是\(\min\{f_{root,1},f_{root,2}\}\)。將每一個聯通塊的答案加起來就好了。

代碼:

#include<bits/stdc++.h>
#define ll long long
#define N 20005
#define M 25005

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

int n,m,C[N];
struct road {int to,nxt;}s[M<<1];
int h[N],cnt;
void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;}
int pw[15];
bool vis[N];
int fa[N],dep[N];

int f[12][60000];
void dfs(int v) {
    vis[v]=1;
    if(dep[v]==0) {
        f[0][0]=0;
        f[0][1]=1e9;
        f[0][2]=C[v];
    } else {
        for(int i=h[v];i;i=s[i].nxt) {
            int to=s[i].to;
            if(vis[to]) fa[v]|=1<<dep[to];
        }
        for(int S=0;S<pw[dep[v]+1];S++) f[dep[v]][S]=1e9;
        for(int S=0;S<pw[dep[v]];S++) {
            if(f[dep[v]-1][S]>=1e9) continue ;
            bool flag=0;
            for(int i=0;i<dep[v];i++) if((fa[v]>>i&1)&&S/pw[i]%3==2) flag=1;
            f[dep[v]][S+flag*pw[dep[v]]]=min(f[dep[v]][S+flag*pw[dep[v]]],f[dep[v]-1][S]);
            int T=S+2*pw[dep[v]];
            for(int i=0;i<dep[v];i++) if((fa[v]>>i&1)&&S/pw[i]%3==0) T+=pw[i];
            f[dep[v]][T]=min(f[dep[v]][T],f[dep[v]-1][S]+C[v]);
        }
    }
    int flag=0;
    for(int i=h[v];i;i=s[i].nxt) {
        int to=s[i].to;
        if(vis[to]) continue ;
        dep[to]=dep[v]+1;
        dfs(to);
        for(int S=0;S<pw[dep[v]+1];S++) {
            f[dep[v]][S]=min(f[dep[to]][S+pw[dep[to]]],f[dep[to]][S+2*pw[dep[to]]]);
        }
    }
}

int main() {
    pw[0]=1;
    for(int i=1;i<=10;i++) pw[i]=pw[i-1]*3;
    n=Get(),m=Get();
    for(int i=1;i<=n;i++) C[i]=Get();
    for(int i=1;i<=m;i++) {
        int a=Get(),b=Get();
        add(a,b),add(b,a);
    }
    int ans=0;
    for(int i=1;i<=n;i++) {
        if(vis[i]) continue ;
        dfs(i);
        ans+=min(f[0][1],f[0][2]);
    }
    cout<<ans;
    return 0;
}
相關文章
相關標籤/搜索