【圖上狀壓dp】2021CCPC女生賽C題連鎖商店
阿新 • • 發佈:2021-11-08
連鎖商店
vp時候dfs了一發,線性dp了一發。dfs的時間複雜度是n!,圖上怎麼線性dp...
當時考慮到了狀壓,但是想把·每個景點直接狀壓,時間複雜度是2^32,就沒有多想。但實際上應該把每種公司狀壓,這樣若一種公司只出現了一次則取紅包一定是最優的。只有在公司出現次數大於等於2的時候才考慮取和不取,也就是使用狀壓,而公司出現次數最多為36/2=18次。
設狀態方程f[i][S]為從1走到i點,第二類公司的狀態為S時的最大紅包金額
點選檢視程式碼
int g[N][N]; int c[N]; int w[N]; map<int,int>mp; bool st[N]; int f[N][1<<18];int idx;int id[N],dig[N]; int main(){ int n,m,u,v; cin>>n>>m; for(int i=1;i<=n;i++) cin>>c[i],mp[c[i]]++; for(int i=1;i<=n;i++) if(mp[i]>=2) id[idx] =i , dig[i] =idx++; //離散化第二類公式,方便做狀壓 for(int i=1;i<=n;i++) if(mp[c[i]]==1)st[i]=1;//標記第一類 for(int i=1;i<=n;i++) cin>>w[i]; while (m--){ cin>>u>>v; g[u][v]=1; } for (int k = 1; k <= n; k++) { //floyd跑出兩點之間是否聯通 for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (i != j) g[i][j] |= (g[i][k] & g[k][j]); } } } //初始化第一個節點 if(st[1]) f[1][0] = w[c[1]]; else f[1][1<<dig[c[1]]] = w[c[1]]; //從前往後更新 for(int u=1;u<=n;u++){ for(int S=0;S<1<<idx;S++){ for(int v=1;v<=n;v++){ if(!g[u][v]) continue; if(st[v]) f[v][S] = max(f[v][S],f[u][S] +w[c[v]]); else{ if(!((S>>dig[c[v]])&1)){ f[v][S|(1<<dig[c[v]])] = max(f[v][S|(1<<dig[c[v]])],f[u][S] +w[c[v]]); } } } } } for (int i = 1; i <= n; i++) { int ans = 0; for (int S = 0; S < 1 << idx; S++) ans = max(ans, f[i][S]); cout << ans << endl; } return 0; }