1 条题解
-
0XzyStudio (admin) LV 8 MOD RP++ 机房蒟蒻 tayz @ 2024-11-23 14:12:37
一个重要的观察是,我们永远不会移动同一个元素两次, 因此最终的解就是移动元素的最小数量 。
另一个关键点是,我们实际上不需要逐个执行操作。可以假设我们首先将一些元素取出,然后将它们插入到我们选择的某个位置。
对于一些特殊情况,如果数组中至少包含一个 0 和一个 1,但没有 2,则无法找到有效解。如果数组中至少有一个 2,我们可以将所有 0 移到数组的左侧,将所有 1 移到右侧。若数组仅包含 0 或仅包含 1,那么不需要任何移动,答案为 0。
在我们的解决方案中,我们假设至少有一个 0 和一个 1 不被移动。因此,当最优选择是移动所有的 0(或者所有的 1,这两种情况类似)时,需要处理一些特殊情况。如果我们移动所有的 0,可以将它们插入到两个 2 之间(如果存在这样的两个 2)。否则,我们需要执行一次额外的移动:取出一个 2,将其移到数组的末尾,然后将所有 0 放在后面。如果数组的第一个(或最后一个)元素已经是 2,则无需进行额外的移动。
一般情况下,我们假设至少有一个 0 和一个 1 不被移动。我们选择删除部分 0、部分 1 以及部分 2。左边的数组(最终状态)可能会存在一些相邻的冲突元素对。删除的 2 的数量需要等于或多于这些冲突的数量。在这种情况下,我们可以找到一个有效的解决方案,因为可以将所有的 0 插入到未移动的某个 0 的旁边(对 1 也执行相同的操作)。
我们的方法是先移除所有的 2,并在它们的位置留下空白。因此,初始成本等于数组中 2 的总数。接下来,从左到右遍历数组。当遇到 0 或 1 时,我们可以选择删除或保留它们。如果保留,我们可能需要在其前面插入一个 2 来解决潜在的冲突。当到达空白位置时,可以选择不进行操作并继续,或者插入一个 2。后者的选择会使成本减少 1,就像一开始没有移动该元素一样。
动态规划的解决方案开始成型:
设 \(d[\text{len}][\text{numTwo}][\text{last}][\text{mask}]\) 表示处理长度为 \(\text{len}\) 的前缀时的最小成本,其中:
- \(\text{numTwo}\) 是我们插入的 2 的数量;
- \(\text{last}\) 是前缀最后一个元素的值;
- \(\text{mask}\) 是一个二进制掩码,两位分别表示是否至少有一个 0 和一个 1 未被移动。
最终解可以通过检查 \(d[N][\text{totalNumTwo}][0/1/2][3]\) 来获得。递归的计算时间为 \(O(1)\),因此时间复杂度为 \(O(N^2)\) 。
注意到对于某个长度 \(\text{len}\) 的所有值,仅依赖于 \(\text{len}-1\) 的值,所以滚掉一维,可以将内存优化为 \(O(N)\) 。
#include<bits/stdc++.h> #define IXcape cout<<endl;return 0 #define Venti cout<<"\nVenti!\n" #define int long long using namespace std; const int V=5e3+10; int cnt[4],a[V],dp[V][4][3],tmp[V][4][3]; int t,n,res,ans; inline int check(){ if(cnt[2]==0&&cnt[0]!=0&&cnt[1]!=0){ cout<<-1<<'\n'; return 1; } if(cnt[0]==0||cnt[1]==0){ cout<<0<<'\n'; return 1; } return 0; } signed main() { freopen("sonnet.in","r",stdin); freopen("sonnet.out","w",stdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>t; while(t--){ memset(cnt,0,sizeof cnt); res=0; cin>>n; for(int i=0;i<n;i++){ cin>>a[i]; cnt[a[i]]++; } if(check()==1) continue; for(int i=0;i<n-1;i++) if(a[i]==2&&a[i+1]==2) res=1; ans=min(cnt[0],cnt[1]); if(res==0) ans+=(a[0]!=2&&a[n-1]!=2); for(int j=0;j<=cnt[2];j++) for(int res=0;res<=3;res++) for(int la=0;la<=2;la++) dp[j][res][la]=(res==3)?0:n; for(int i=n-1;i>=0;--i){ for(int j=0;j<=cnt[2];j++) for(int res=0;res<=3;res++) for(int la=0;la<=2;la++){ int ans=n; if(a[i]==2){ if(j!=0) ans=min(ans,dp[j-1][res][2]-1); ans=min(ans,dp[j][res][la]); } else{ if(la==a[i]||la==2) ans=min(ans,dp[j][res|(1<<a[i])][a[i]]); else if(j!=0) ans=min(ans,dp[j-1][res|(1<<a[i])][a[i]]); ans=min(ans,dp[j][res][la]+1); } tmp[j][res][la]=ans; } for(int j=0;j<=cnt[2];j++) for(int res=0;res<=3;res++) for(int la=0;la<=2;la++) dp[j][res][la]=tmp[j][res][la]; } ans=min(ans,cnt[2]+dp[cnt[2]][0][2]); cout<<(ans>=n?-1:ans)<<'\n'; } IXcape; }
- 1
信息
- ID
- 1091
- 难度
- 9
- 分类
- (无)
- 标签
- (无)
- 递交数
- 1
- 已通过
- 1
- 通过率
- 100%
- 上传者