題目描述
定義長度為 \(n\) 的好串 \(s\) 滿足:
給你長度為 \(n\) 的序列 \(a\) 和 \(v\),分別表示原序列和價值序列。你每次可以選擇乙個原序列中的好串,將其刪除之後剩下的串會前後拼接。設這次刪除的長度是 \(l\),那麼會得分 \(v_l\),問最大得分,不一定要把原序列刪完。
\(n\leq 400,|v_i|\leq 10^5,a_i\leq10^9\)
解法
考慮求出 \(f[l][r]\) 表示把 \([l,r]\) 刪完的最大得分,然後再用一維 \(dp\) 就可以拼出答案。
好串其實就是先以 \(1\) 的斜率上公升、再以 \(1\) 的斜率下降的尖角形。發現還是不好做,我們可以考慮把它們拆成上公升部分和下降部分,然後拼起來,拆分後的問題還是可以用 \(dp\) 解決。
具體來說我們設 \(up[l][r]\) 表示獲取以 \(a_l\) 開頭 \(a_r\) 結尾的上公升段的最大價值,\(dn[l][r]\) 表示獲取 \(a_l\) 開頭 \(a_r\) 結尾的上公升段的最大價值,那麼轉移列舉 \(i\) 使得 \(a_i+1=a_r/a_i-1=a_r\):
\[up[l][r]\leftarrow up[l][i]+f[i+1][r-1]
\]\[dn[l][r]\leftarrow dn[l][i]+f[i+1][r-1]
\]那麼如何把他們拼起來呢?我們列舉最高點 \(i\) 使得 \(a_i\geq a_l\and a_i\geq a_r\),那麼好串的長度一定是 \(2\cdot a_i-a_l-a_r\),很容易寫出轉移:
\[f[l][r]\leftarrow up[l][i]+dn[i][r]+v[2\cdot a_i-a_l-a_r+1]
\]但是 \(a_l,a_r\) 也可以不在乙個好串中,這時候我們需要列舉分界點將他們分開:
\[f[l][r]\leftarrow f[l][i]+f[i+1][r]
\]此外只有上公升段和下降段需要單獨討論一下,時間複雜度 \(o(n^3)\)
總結
設計多個 \(dp\) 狀態,互相轉移的方法是很值得思考的。其實我感覺它的原理還是__分步思想__,把乙個較為複雜的問題拆分成若干個部分解決,這些部分中可能又蘊含子問題。
#include #include using namespace std;
const int m = 405;
const int inf = 0x3f3f3f3f;
int read()
while(c>='0' && c<='9')
return x*f;
}int n,a[m],v[m],dp[m],f[m][m],up[m][m],dn[m][m];
void upd(int &x,int y)
signed main()
if(a[l]>=a[r])
upd(f[l][r],dn[l][r]+v[a[l]-a[r]+1]);
if(a[l]<=a[r])
upd(f[l][r],up[l][r]+v[a[r]-a[l]+1]);
} for(int i=1;i<=n;i++)
printf("%lld\n",dp[n]);
}
省選集訓2022 模擬賽8
題目描述 給定乙個 n times m 的 01 矩陣,對於矩陣的每乙個位置,你需要對於這個位置上的值反轉,然後求出這個矩陣的秩的變化 0 可以將這個矩陣看成 n 個大小為 0,2 m 的數,秩就是它們線性基的大小。n,m leq 1000 解法 我們判斷求出原先的 n 個向量在原來的線性基中是可以...
省選集訓2022 模擬賽10
題目描述 給定 n 個元素,每個元素有兩個屬性值 a i,b i 我們可以將其以任意順序排列,要最大化下式 min a i i cdot k max b i i cdot k n leq 10 5,a i,b i,kn leq 10 9 解法 應該是遇到困難退大火才對,直接使用列舉法,考慮列舉 m ...
省選集訓2022 模擬賽11
題目描述 n 個城市構成一棵樹,現在要求在一些城市中設定監測點,使得每個城市可以通過到監測點的距離區分出來 不同可以知道是到哪個監測點的距離,可以模擬為樹上的座標 給定 q 次修改,每次斷開邊 u,v 再連上邊 x,y 然後求出最小設定的監測點數目。解法 無根樹問題考慮定根,我們先列舉根強制根選取,...