Problem J. 屠龙勇者 ErvinXie(队列移动----在某个数组上走最小步数拿到满足要求的东西)

题意:有k种金属,需要用s个金属构造一个阵法(金属可能重复),
然后一条河,每一米都有一种且一个金属,问从任意一个点开始能否找到满足阵法要求的金属,如果能够满足,输出走的最少步数,否则输出DragonXie
题目
解决:
其实就是从某个点出发,往后走,走最短步数,拿到满足题目要求的金属数量和种类(可能拿的多,如你需要1,2,3金属,河上面是1,2,4,3,你就需要走4步,因为从1开始走,想拿3必须走4;如果你需要1,2,3金属,河上是1,1,2,4,3那么你是从第二个1开始走。)
拿一个队列放在这条河上移动,如果是阵法要求的金属就加入队列,当现在加入的那个金属和头队列一样,且现在这种金属是多于阵法要求的数量的,就把头队列那个金属pop掉,如果某次加入某个金属使得队列中的金属种类和数量>=阵法的要求,就记录下这次从这个点出发往后走过的步数,每次取min就好了。

#include <iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<set>
#include<map>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int N=3e6+9;
struct node
{
    int pos,data;//pos记录金属的位置,data记录是哪种金属
};
queue<node>que;//代表我手里拿着的金属
set<int>se;//记录我所需要的金属的种类
int arr[N];//存河上的金属
int vis[N];  //记录我现在需要的每种金属的数量,可能为负数,代表我手里这种金属比阵法要求的多
int s,k,len;
int tol,ans;//tol形成阵法所需要的金属个数,ans是每次队列中的金属满足阵法的要求,对每次走过的距离取min
int flag;//判断是否某次可以满足阵法要求,flag为0是不可能形成
void Delete()
{
    while(!que.empty())  
    {
        node q=que.front();
        if(vis[q.data]<0)//如果头队列的那种金属目前是多余阵法的要求的,就删除
        {                 //一直循环到某个金属不多为止
            vis[q.data]++;
            que.pop();
        }
        else break;
    }
}
void judge(int r)
{
    if(tol==s)  //如果队列中的金属个数满足阵法要求
    {
        flag=1;   
        int l=que.front().pos; //l是我从哪个位置开始走
        ans=min(ans,r-l+1); //走的距离
        vis[que.front().data]++;//这个时候把头队列这个金属给扔掉,破坏这次形成的阵法,准备开始下一次,看是否有更短的步数
        que.pop();  
        tol--;   //经过上面那个函数的while循环,这个头队列一定是必须的金属,删掉后tol--
        Delete();//删掉头队列后,头队列后面的金属的数量可能是多余的
    }
}
void solve()
{  
    ans=inf; //先开最大
    scanf("%d%d",&k,&s);
    for(int i=0; i<s; i++)
    {
        int data;
        scanf("%d",&data);
        vis[data]++;    //每种金属需要多少个,为0的时候是已经够了,负数的时候是你多拿了
        se.insert(data);  //记录需要的种类
    }
    scanf("%d",&len);
    for(int i=0; i<len; i++)
    {
        int data;
        scanf("%d",&data);
        arr[i+1]=data;
    }
    for(int i=1; i<=len; i++)
    {
        if(se.count(arr[i])>0)//如果这种金属是阵法要求的,就加入队列
        {
            node q;
            q.pos=i;
            q.data=arr[i];
            que.push(q);
            vis[arr[i]]--;//这种金属减去一,代表我需要的少一个,可能为负数,代表我现在手里的这种金属是多的
            if(vis[arr[i]]>=0)tol++;//>=0是因为我加入队列的金属,是我现在阵法必须要的,而不是多余的,那么tol++
            Delete();//删除我手里多余的金属,从头开始删
            judge(i); //判断这个时候队列中的金属是否满足阵法的要求,i是加入最后一个金属的位置。
        }
    }
    if(flag==1)printf("%d\n",ans);
    else printf("DragonXie\n");
}
int main()
{
    solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值