1. 程式人生 > 實用技巧 >2020牛客多校第六場K.K-Bag (思維?)

2020牛客多校第六場K.K-Bag (思維?)

https://ac.nowcoder.com/acm/contest/5671/K

題意:

一個序列被稱為k-bag,當且僅當它以一些(可能是一個)1~k的排列時。例如,1,2,3,2,1,3,3,2,1是有效的3-bag序列。部分k-bag序列是指k-bag的連續子序列。

給出長度為n的序列,判斷該序列是否為部分k-bag序列。

思路:

b陣列代表以i結尾的前k個字元是否有重複的,(下標小於k的前面的數字預設不同)沒有重複為1,有重複數字為0;

從後往前找第一個重複數字的位置為top,從這個位置擴k位,如果top+k-1>n,那麼 (n , top+k-1)之間的b陣列的值為0;

下標用i表示,i%k的位置代表從 i%k 這個位置開始後面是完整的,如果b[i%k]=0,那麼就不能從這個位置開始,如果這樣的位置大於等於k,代表不能從前面k個數開始,就不合法。

所以:下標%k的位置為0的個數(相同位置不能重複計算)大於等於k,則不合法。

當n<k(k很大時,不能往後補位),只會是一個不完整的部分或者兩個不完整的部分,則從後往前找到第一個重複數字的位置,判斷前面的數字是否重複。

程式碼:

#include <bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
typedef long long ll;
const int inf=0x3f3f3f3f;
const long long INF=0x3f3f3f3f3f3f3f3f;
const int MAXN=1e6+5
; const double eps=-1e8; typedef pair<int,int>pii; int a[MAXN],b[MAXN]; int vis[MAXN]; unordered_map<int,int>mp;//map會超時 int main() { int T; scanf("%d",&T); while(T--) { mp.clear(); int n,k; scanf("%d%d",&n,&k); int flag=0;
for(int i=1;i<=n;i++) { scanf("%d",&a[i]); if(a[i]>k)flag=1; } for(int i=0;i<=2*n+2;i++)b[i]=1,vis[i]=0; if(flag) { printf("NO\n"); continue; } int cnt=0; flag=0; for(int i=1,j=1;i<=n;i++)//判斷以i結尾的前k個數字是否重複 { if(mp[a[i]]==0)cnt++; mp[a[i]]++; if(i>k) { if(mp[a[i-k]]==1)cnt--; mp[a[i-k]]--; } if(cnt!=k)b[i]=0; if(mp[a[i]]==2)flag=1; if(flag==0)b[i]=1; } mp.clear(); int top=-1; for(int i=n;i>=1;i--)//從後往前第一個重複數字的位置 { mp[a[i]]++; if(mp[a[i]]==2) { top=i; break; } } if(n>k) { cnt=0; if(top!=-1) { for(int i=n+1;i<top+k;i++) { b[i]=0; } } for(int i=1;i<=2*n+1;i++) { if(b[i]==0&&vis[i%k]==0) { cnt++; vis[i%k]=1; } } if(cnt<k)printf("YES\n"); else printf("NO\n"); } else //n<k 後一段無重複數字,判斷前半段是否有重複數字 { flag=0; mp.clear(); for(int i=1;i<=top;i++) { mp[a[i]]++; if(mp[a[i]]>1){flag=1;break;} } if(flag)printf("NO\n"); else printf("YES\n"); } } return 0; } /* 10 4 6 1 2 3 3 4 6 1 6 6 1 7 8 1 2 3 3 4 5 6 7 8 1 2 3 3 2 1 1 7 8 1 2 3 3 2 1 4 7 4 4 4 4 3 2 1 4 */
View Code