《破曉傳說》角色定位及實用技能推薦 角色培養方向介紹
動態規劃簡介:動態規劃是在一個困難的 巢狀決策鏈 中,決策出最優解。動態規劃有 可推導性 ,但同時,動態規劃也有 無後效性 ,即 每個當前狀態會且僅會決策出下一狀態,而不直接對未來的所有狀態負責 。
子序列問題
首先宣告2個名詞:
$ \operatorname{LIS} $ :Longest Increasing Subsequence 最長遞增子序列
$ \operatorname{LCS} $ :Longest Common Subsequence 最長公共子序列
我們知道,用樸素的 $ \operatorname{DP} $ 解 $ \operatorname{LIS} $ 和 $ \operatorname{LCS} $ ,複雜度都是 \(O(n^2)\)
for(int i=1;i<=n;i++) { dp[i]=1; for(int j=1;j<i;j++) if(val[j]<val[i] && dp[i]<dp[j]+1) dp[i]=dp[j]+1,pre[i]=j; } // LIS for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { dp[i][j]=max(dp[i-1][j],dp[i][j-1]); if(a[i]==b[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1); } } // LAS
↑ 這便是極為低效的 \(O(n^2)\) 做法
考慮用 \(O(n log n)\) 演算法:用 \(dp[i]\) 表示在該序列中,上升子序列長度為 \(i\) 的上升子序列,的最小末尾數值。這樣可以二分求出比當前數值小的最長子序列。
$ \operatorname{LIS} $ 核心程式碼:
memset(dp,inf,sizeof(dp)); dp[0]=0; for(int i=1;i<=n;i++) { if(val[i]>dp[ans]) dp[++ans]=val[i]; // 比之前最長的子序列的最大值還大,則 ans += 1 else { int l=0,r=ans,ret=0; while(l<r) { int mid=(l+r)/2; if(dp[mid]<=val[i]) l=mid+1; // 找到第一個比 dp[...] 小的數 (這一題中是嚴格遞增) else r=mid-1,ret=mid; } dp[l]=min(dp[l],val[i]); } }
在特殊條件下,即兩個序列的元素種類相同,可以用類似離散化的方式將一個序列變為 \(1…n\) 的序列,此時的 $ \operatorname{LCS} $ 就是另一個序列的 $ \operatorname{LIS} $ 則在這種條件下可以 \(O(nlogn)\) 地處理 $ \operatorname{LCS} $ 問題
$ \operatorname{LCS} $ 核心程式碼:
memset(dp,inf,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)
{
if(Turn[b[i]]>dp[ans]) dp[++ans]=Turn[b[i]]; // 比之前最長的子序列的最大值還大,則 ans += 1
else
{
int l=0,r=ans,ret=0;
while(l<r)
{
int mid=(l+r)/2;
if(dp[mid]<=Turn[b[i]]) l=mid+1; // 找到第一個比 Turn[b[i]] 小的數
else r=mid-1,ret=mid;
}
dp[l]=min(dp[l],Turn[b[i]]);
}
}
LIS變形——平面吃豆子問題
題意:給定一張平面直角座標系,你現在處在 \((0,0)\) 的位置,而有些網格點上有豆子。如果你在 \((x,y)\) ,只能前往 \((x+1,y),(x,y+1),(x+1,y+1)\) 。請問最多能夠吃到多少顆豆子?
\(n\le 10^5,-10^9\le x_i,y_i \le 10^9\) 。
首先找出 \(x_i\ge 0,y_i\ge 0\) 的所有點,並對他們根據 \(x\) 為第一關鍵字,\(y\) 為第二關鍵字排序。
之後就對所有 \(y\) 跑一邊 \(\operatorname{LIS}\) 就可以啦
$\texttt{code}$
#define inf 0x3f3f3f3f
#define Maxn 100005
int n,m,ans,dp[Maxn];
struct Data { int x,y; }a[Maxn];
bool cmp(Data x,Data y)
{
if(x.x!=y.x) return x.x<y.x;
return x.y<y.y;
}
int main()
{
n=rd();
for(int i=1,x,y;i<=n;i++)
{
x=rd(),y=rd();
if(x<0 || y<0) { i--,n--; continue; }
a[i]=(Data){x,y};
}
sort(a+1,a+n+1,cmp);
memset(dp,inf,sizeof(dp)),dp[0]=0;
for(int i=1;i<=n;i++)
{
if(a[i].y>=dp[ans]) dp[++ans]=a[i].y;
else
{
int nl=0,nr=ans,mid,ret=0;
while(nl<=nr)
{
mid=(nl+nr)>>1;
if(dp[mid]<=a[i].y) nl=mid+1;
else nr=mid-1,ret=mid;
}
dp[ret]=min(dp[ret],a[i].y);
}
}
printf("%d\n",ans);
return 0;
}