【DP,求方案】AcWing 316. 減操作
阿新 • • 發佈:2022-05-05
分析
這題可以拆成兩道題來做:
第一題
首先,看到這個合併順序,感覺十分不好下手,那麼我們不妨繞過對合並的分析,看看問題能等價為什麼:
隨便寫幾個柿子,比如 \(1-((1-4)-(5-(1-4)))\),發現運算的結果可以化到最簡表述為沒有括號只有 +-
的形式,而且加減在除了第一個位置必然是減(因為最後合併的一定需要包括位置 \(1\))每個位置都可以出現,不妨大膽猜想:問題等價於——除了第一個位置,其他位置可以任意放置 +/-
。
下面是證明,感覺顯然的話可以繞過。
證明:給你一個長度為 \(n\) 的序列,支援在中間的 \(n-1\) 個位置新增 +/-
,其中第一個一定為 -
,可以將其等價為支援選擇鄰項作減法的序列。
採用數學歸納法,記長度為 \(k\)
- 顯然 \(k = 2, 3\) 時成立。
- 假設 \(k = n\) 時成立。
- 對於 \(k = n + 1\),由歸納假設,字尾 \(n\) 項能夠表述為支援在中間的 \(n-1\) 個位置任意新增
+/-
,其中第一個一定為-
的序列。
為了方便表述,這裡對 \(k=5\) 的情況(
A B C D E
)作解釋:
首先A, B
間一定為-
:A - B C D E
。
對B C
之間的符號作分類討論:
-
B + C
:那麼我們就規定合併順序為A - (B - C D E)
,可以發現字尾四項就是 \(k=4\) 的情況,滿足歸納假設。 -
B - C
:那麼就率先合併A B
,情況化歸為 \(k = 4\)
可以發現上面的歸納過程正好能夠為我們構造方案提供幫助。
第二題
揹包 dp。
這題沒什麼需要注意的,就每個物品分別當成是正體積和負體積的 \(01\) 揹包問題處理即可,當然因為陣列下標非負,我們需要加一個足夠大的偏移量,這裡規定是 \(100\times 100\)。
注意到需要輸出方案,所以我們記錄一下每個狀態的前驅 pre
,然後利用 pre
得到 +-
序列並構造出解即可。(構造出解的思路可以見上面的證明)。
// Problem: 減操作 // Contest: AcWing // URL: https://www.acwing.com/problem/content/318/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include<bits/stdc++.h> using namespace std; #define debug(x) cerr << #x << ": " << (x) << endl #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define dwn(i,a,b) for(int i=(a);i>=(b);i--) #define pb push_back #define all(x) (x).begin(), (x).end() #define x first #define y second using pii = pair<int, int>; using ll = long long; inline void read(int &x){ int s=0; x=1; char ch=getchar(); while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();} while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar(); x*=s; } const int N=110, M=20005, del=10000; int n, V; int w[N]; bool f[N][M]; pii pre[N][M]; bool in(int val){ return val>=0 && val<M; } string str; void get_str(int x, int y){ if(x==1) return; auto [tx, ty]=pre[x][y]; get_str(tx, ty); if(ty<y) str+="+"; else str+="-"; } void print(int u, int d, bool fl){ if(u==n) return; if(u+1==n){ cout<<u-d<<endl; return; } bool nx=(str[u+1]=='+')^fl; if(nx) print(u+1, d, fl^1), cout<<u-d<<endl; else cout<<u-d<<endl, print(u+1, d+1, fl); } int main(){ cin>>n>>V; rep(i,1,n) read(w[i]); f[1][w[1]+del]=1; rep(i,2,n){ int t=w[i]; if(i>2){ rep(j,0,M-1) if(in(j-t) && !f[i][j] && f[i-1][j-t]){ f[i][j]=1; pre[i][j]={i-1, j-t}; } } t=-t; rep(j,0,M-1) if(in(j-t) && !f[i][j] && f[i-1][j-t]){ f[i][j]=1; pre[i][j]={i-1, j-t}; } } get_str(n, V+del); str=" "+str; print(1, 0, 0); return 0; }