這道題最初的思路是用數值大的打數值小的,而且這一打不能使數值大的那個浪費,先對 \(r\) 陣列進行排序(規定從第 \(1\) 項開始)。
sort(r+1,r+1+n);
然後r陣列就成了一個不嚴謹的單調遞增序列,只需要將 \(r_{i}\) 攻擊\(r_{i-1}\)項就行,如果 \(r_{i}=r_{i-1}\) ,那麼 \(r_{i}和r_{i-1}\)兩個元素無法死亡。
考慮dp,我們用 \(\mathrm {f} _{i}\) 表示在排過序的 \(r\) 陣列中,從第一項到第 \(i\) 項時,最多會死掉多少個元素,易得到狀態轉移方程式為
\( f_{i}=\left \{ \begin{matrix} f_{i-1} & ,r_{i}=r_{f_{i-1}+1} \\ f_{i-1}+1& ,r_{i}>r_{f_{i-1}+1} \\ \end{matrix} \right. \)
那麼解釋一下這個方程式,\(f_{i}\)既然可以表示最多死亡個數,那麼死亡的元素一定是陣列中比較小的,截至到 \(r_{i}\) ,死亡的元素是 \(r[1,f_{i}]\) ,如果 \(r_{i}=r_{f_{i-1}+1}\) ,就說明兩個即將對戰的元素相等,那麼我們就知道這兩個元素誰都打不死誰,所以 \(f_{i}=f_{i-1}\) . 否則就說明 \(r_{i}\) 可以擊敗 \(r_{f_{i-1}+1}\),所以\(f_{i}=f_{i-1}+1\).
當 \(i\) 為 \(1\) 時,答案一定是 \(1\),所以 \(i\) 從 \(2\) 開始遍歷,最後輸出答案\(n-f_{n}\)即可。
憑藉這個思路,複雜度控制在了 \(O(n\log_{2}{n}+ n)\)
AC程式碼:
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
int r[100005];
int dp[100005];
int main(){
//freopen("duel.in","r",stdin);
//freopen("duel.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&r[i]);
sort(r+1,r+n+1);
for (int i=2;i<=n;i++) dp[i] = r[dp[i-1]+1]==r[i]?dp[i-1]:dp[i-1]+1;
printf("%d",n-dp[n]);
return 0;
}