题意: 有n种调料放在冰箱,你最多能把k种调料放外面,你每次打开冰箱,可以拿出一种调料,同时也可以放回去一种调料,也可以不放回。 每组输入案例第一行为n,k,第二行为n种调料的编号(从1到1e9),需要调料的顺序是输入的循序,输出是最少要打开多少次。
对于知道使用情况的置换算法,最优解是找一个最后需要使用的物品替换掉也就是,如果一个物品后面已经不需要用到,就要放回去了,当外面的物品达到了k个,此时某些情况我就得从外面中拿一个回去
思路:
离散化:看到a[i]比较大,而个数比较少,并且只需要知道相对编号就好,这时就想到了离散化
next数组下一个相同值在哪个位置;
last数组用来辅助next数组记录,一开始所有的last[a[i]]==inf,只要大于a[i]编号(a[i]的编号通过离散化只会最大从0~(n-1))就好了,所以这里我用了last[a[i]]=n;
inqueue数组用来判断是否物品在外面(即在队列中)
具体细节见代码
AC_Code
1 #include <cstring> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <iostream> 5 #include <algorithm> 6 #include <bits/stdc++.h> 7 using namespace std; 8 typedef long long ll; 9 const int maxn=1e5+5; 10 int a[maxn],b[maxn],nxt[maxn],last[maxn],inqueue[maxn]; 11 12 map<int,int>mp; 13 struct node{ 14 int nxt,id; 15 bool operator < (const node &rhs ) const { 16 return nxt<rhs.nxt; //按下一次需要的"时间"从大到小排序 17 } 18 }; 19 20 int main() 21 { 22 int t,n,k,ans; 23 scanf("%d",&t); 24 while( t-- ){ 25 ans=0; 26 priority_queue<node>Q; 27 memset(inqueue,0,sizeof(inqueue)); 28 mp.clear(); 29 30 int cnt=0; 31 scanf("%d%d",&n,&k); 32 // for(int i=0;i<n;i++){ //离散化 33 // scanf("%d",&a[i]); 34 // if( !mp.count(a[i])){ 35 // mp[a[i]]=cnt++; 36 // } 37 // a[i]=mp[a[i]]; 38 // last[a[i]]=n; 39 // } 40 41 for(int i=0;i<n;i++){ //这种离散化方式更快 42 scanf("%d",&a[i]); 43 b[i]=a[i]; 44 } 45 sort(b,b+n); 46 int reu=unique(b,b+n)-b; 47 for(int i=0;i<n;i++){ 48 a[i]=lower_bound(b,b+reu,a[i])-b; 49 last[a[i]]=n; 50 } 51 for(int i=n-1;i>=0;i--){ //设定next 52 nxt[i]=last[a[i]]; 53 last[a[i]]=i; 54 } 55 56 for(int i=0;i<n;i++){ 57 if(inqueue[a[i]]){ //如果要拿出的物品已经外面有了,那就把这个也压入队列, 58 //因为next值发生了变化,也要更新。这是a[i]最新的next值 59 Q.push((node){nxt[i],a[i]}); 60 continue; 61 } 62 else if( k ){ //在外面的物品不够,直接拿出来 63 Q.push((node){nxt[i],a[i]}); 64 inqueue[a[i]]=1; 65 k--; 66 ans++; 67 } 68 else{ //在外面的物品够了,则要进行放进去,拿出来的操作 69 node x=Q.top(); 70 Q.pop(); 71 inqueue[x.id]=0; 72 Q.push((node){nxt[i],a[i]}); 73 inqueue[a[i]]=1; 74 ans++; 75 } 76 } 77 printf("%d ",ans); 78 } 79 return 0; 80 }