AtCoder AGC035D Add and Remove (状压DP)
題目鏈接
https://atcoder.jp/contests/agc035/tasks/agc035_d
題解
想了兩小時(shí)憋出來(lái)一個(gè)狀壓DP,發(fā)現(xiàn)人家怎么空間才十幾MB,原來(lái)暴力就行了。。。
考慮原序列那個(gè)操作,我們可以建一個(gè)圖,一開(kāi)始有\(n\)個(gè)點(diǎn)沒(méi)有邊,每次選一個(gè)點(diǎn)向其左右未被選的點(diǎn)加兩條邊,一個(gè)點(diǎn)的貢獻(xiàn)次數(shù)就是它到左右兩個(gè)終點(diǎn)的路徑數(shù)。
那么我們可以枚舉最后選的點(diǎn),把原序列分裂成兩個(gè)區(qū)間,因此使用區(qū)間DP. 現(xiàn)在的問(wèn)題是我們需要方便地統(tǒng)計(jì)一個(gè)點(diǎn)對(duì)總和的貢獻(xiàn)。我們觀察到,從\([l,r]\)這一層到\([1,n]\)最底層,每次要么拓展左邊(左端點(diǎn)\(l\)連向新的\(l'\),\(r\)連向\(l'\)),要么拓展右邊,這樣總共的過(guò)程可以表達(dá)為一個(gè)長(zhǎng)度不超過(guò)\((n-1)\)的01串,且從\(l\)或\(r\)到達(dá)\(1\)或\(n\)的方案數(shù)可由這個(gè)串得到(具體地,維護(hù)兩個(gè)變量\(x=1,y=1\), 拓展左邊時(shí)\((x,y)\rightarrow (x+y,y)\), 拓展右邊時(shí)\((x,y)\rightarrow (x,x+y)\))。那么預(yù)處理每個(gè)01串對(duì)應(yīng)的系數(shù),就可以快速計(jì)算了。最終的DP狀態(tài)是,\(f[l][r][k][S]\)表示區(qū)間\([l,r]\),01串長(zhǎng)度為\(k\),串本身為\(S\).
總共狀態(tài)數(shù)是\(\sum^n_{i=1}2^i(n-i+1)=O(2^nn)\)的,轉(zhuǎn)移需要\(O(n)\)的復(fù)雜度(其實(shí)遠(yuǎn)遠(yuǎn)不滿),總時(shí)間復(fù)雜度\(O(2^nn^2)\).
但是如果我們?cè)O(shè)\(f[l][r][k][S]\)記憶化的話空間復(fù)雜度就變成\(O(2^nn^3)\)了,怎么辦?智障的我就把后兩維壓到一起了,卡著空間限制過(guò)了……
然而事實(shí)是重復(fù)遍歷一個(gè)狀態(tài)的情況很少,不記憶化不僅能過(guò),而且速度還快一倍。(如果不記憶化的話不需要預(yù)處理數(shù)組,把\(S\)串直接改成左右端點(diǎn)分別被算幾次就行了)
實(shí)測(cè)效果如下:
(上:不記憶化 下:記憶化)
枯了
代碼
記憶化:
#include<bits/stdc++.h> #define llong long long #define mkpr make_pair #define riterator reverse_iterator #define U ((1<<n)-1) using namespace std;inline int read() {int x = 0,f = 1; char ch = getchar();for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}return x*f; }const int N = 18; const llong INF = 1e17; llong f[N+1][N+1][(1<<N)+3]; llong val[(1<<N)+3]; llong a[N+3]; int n;void updmin(llong &x,llong y) {x = min(x,y);}llong dfs(int l,int r,int sta) {if(f[l][r][sta]<INF) {return f[l][r][sta];}if(r-l<=1) return f[l][r][sta]=0ll;for(int i=l+1; i<r; i++){updmin(f[l][r][sta],dfs(l,i,((sta<<1)|1)&U)+dfs(i,r,(sta<<1)&U)+a[i]*val[sta]);}return f[l][r][sta]; }int main() {scanf("%d",&n);for(int i=0; i<(1<<n)-1; i++){int len = n; while(i&(1<<len-1)) {len--;} len--;llong x = 1ll,y = 1ll; for(int j=0; j<len; j++) i&(1<<j)?y+=x:x+=y; val[i] = x+y;}for(int i=0; i<n; i++) scanf("%lld",&a[i]);memset(f,10,sizeof(f));printf("%lld\n",dfs(0,n-1,(1<<n)-2)+a[0]+a[n-1]);return 0; }不記憶化
#include<bits/stdc++.h> #define llong long long #define mkpr make_pair #define riterator reverse_iterator #define U ((1<<n)-1) using namespace std;inline int read() {int x = 0,f = 1; char ch = getchar();for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}return x*f; }const int N = 18; const llong INF = 1e17; llong a[N+3]; int n;void updmin(llong &x,llong y) {x = min(x,y);}llong dfs(int l,int r,llong x,llong y) {if(r-l<=1) return 0ll;llong ret = INF;for(int i=l+1; i<r; i++){updmin(ret,dfs(l,i,x,x+y)+dfs(i,r,x+y,y)+a[i]*(x+y));}return ret; }int main() {scanf("%d",&n);for(int i=0; i<n; i++) scanf("%lld",&a[i]);printf("%lld\n",dfs(0,n-1,1ll,1ll)+a[0]+a[n-1]);return 0; }總結(jié)
以上是生活随笔為你收集整理的AtCoder AGC035D Add and Remove (状压DP)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: AtCoder AGC034D Manh
- 下一篇: AtCoder AGC030E Less