CCF/CSP 201709-2 公共钥匙盒的求解 C++版

这里给出两种实现方式:

(1)使用map<time, multiset<key_id> >这种结构,存储用户输入的借还钥匙的序列(multiset允许相同元素存在,且自排序)。这里的思路是,将借还时间进行统一排序(原因A:time小的先处理;原因B:同一个key出现的次数是偶数,且先出现的一定是借,后出现的是还,且不允许嵌套,或者说一个借后面一定跟着对应的还。所以我可以这么排序),思路是:map内按照time为键进行排序,对于同一时刻,借还钥匙的id按照自小到大的顺序排列。

具体执行起来,由于map是排序的,所以只要顺序的获取map中的元素,然后对value进行操作即可。

value里面也是排过序的,但是由于还钥匙要在借之前,而value里面的序列是把借还的id进行了一个总排序,我们无法分成两段,第一段是按序的还钥匙的id,第二段是借钥匙的id(借钥匙不需要排序)。所以,我们只好进行两次遍历,第一次只处理要还的钥匙,然后将这个id从value中删掉,同时更新这个钥匙的status;第二次只处理借钥匙,然后更新钥匙的status(删除不删除都无所谓)。

这里表示钥匙状态的是一个数组keyStatus,下表对应钥匙id-1,状态只有两个值,0和1,1表示待还,0表示已还或者没有被借出。借还钥匙,都要更新该钥匙对应的状态。

#include<iostream>
#include<stdio.h>
#include <set>
#include <map>
#include <stdlib.h>  
using namespace std;
typedef pair<int, multiset<int> > PAIR;  
 
int main()
{
   int Num_yaoshi,k_teachernum;
   cin>>Num_yaoshi>>k_teachernum;
    // Num_yaoshi=5;k_teachernum=7;

   int *yaoshi_pailie =new int[Num_yaoshi], *keyStatus=new int[Num_yaoshi];
   //初始化钥匙盒,以及每个钥匙的状态
   for(int i=0;i<=Num_yaoshi-1;i++)
   {
       yaoshi_pailie[i]=i+1;
       keyStatus[i]=0;//1 means the key has been borrowed
   }

    bool flag=false;
   int *yaoshibianhao=new int[k_teachernum],*starttime=new int[k_teachernum],
        *timelong=new int[k_teachernum],*endtime=new int[k_teachernum];
    map<int, multiset<int> > sortedList;
    map<int, multiset<int> >::iterator it;
    multiset<int> tmpset;
    multiset<int>::iterator setit;
    //获取输入,并进行转换,然后行程键值对,放到map中
    for(int i=0;i<=k_teachernum-1;i++)
    {
        cin>>yaoshibianhao[i]>>starttime[i]>>timelong[i];
        endtime[i]=starttime[i]+timelong[i];
        //add the yaoshibianhao[i] into map according to the starttime,按照借的时间把keyid放进去
        it=sortedList.find(starttime[i]);
        if(it==sortedList.end()){//not in the map
            //add the PAIR<starttime[i],multiset<yaoshibianhao[i]>> into map
            tmpset.clear();
        }
        else{// the time has appeared in the map, so add the yaoshibianhao[i] into the tmpset
            tmpset=it->second;
            sortedList.erase(it);
        }
        tmpset.insert(yaoshibianhao[i]);
        sortedList.insert(make_pair(starttime[i],tmpset));
        tmpset.clear();
        
        //add the yaoshibianhao[i] into map according to the endtime,按照还的时间将keyid放到map中
        it=sortedList.find(endtime[i]);
        if(it==sortedList.end()){//not in the map
            //add the PAIR<starttime[i],multiset<yaoshibianhao[i]>> into map
            tmpset.clear();
        }
        else{// the time has appeared in the map, so add the yaoshibianhao[i] into the tmpset
            tmpset=it->second;
            sortedList.erase(it);
        }
        tmpset.insert(yaoshibianhao[i]);
        sortedList.insert(make_pair(endtime[i],tmpset));
        tmpset.clear();
        
   }

//test the input,测试用户的输入能否正常存储到map中
/*
    it = sortedList.begin();
    while(it != sortedList.end())
    {
        cout<<it->first<<": "<<it->second.size()<<endl;
        it ++;         
    }

*/
    //对map进行遍历,处理借还钥匙
   for(it = sortedList.begin();it != sortedList.end();)
    {   
        tmpset.clear();
        tmpset=it->second;//
        cout<<"处理时间:"<<it->first<<", 共有:"<<tmpset.size()<<endl;
        
            int j=0;
           //  先处理还钥匙
            for(setit =tmpset.begin(); setit!=tmpset.end();){ 
               //find the keys to be returned and return them
                flag=false;
                //printf("%d",*setit);
                if(keyStatus[(*setit)-1]==1) {//如果该key的状态为1,表示待还,
                    cout<<"归还key_ "<<*setit;
                   keyStatus[(*setit)-1]=0;//更新钥匙状态
                    for(int i=0;i<Num_yaoshi;i++){//这个可以使用一个set来代替,就不用循环了 ,按照顺序,找到钥匙盒中的第一个空位置
                        if(yaoshi_pailie[i]==0) {//
                            cout<<", 放到位置"<<i+1<<endl;
                            yaoshi_pailie[i]=(*setit);//把钥匙放到空位置
                             tmpset.erase(setit++);//从value中删掉这个key
                             flag=true;
                            break;//后面不再需要找空位置了
                        }
                    }
                    if(flag==false) setit++;
                    continue;
                   
                }
                setit++;
            }
        
        //borrow key,借钥匙
        if(tmpset.size()>0){//防止这一时刻只有还钥匙的操作,则还过钥匙后tmpset为空,所以要判断size
            for( setit = tmpset.begin();setit!=tmpset.end();)
            {
                cout<<"id= "<<(*setit)<<" ";
                keyStatus[(*setit)-1]=1;//更新钥匙状态
                for(int i=0;i<Num_yaoshi;i++){//这个可以使用一个set来代替,就不用循环了 ,找到钥匙盒中这个钥匙的位置
                    if(yaoshi_pailie[i]==(*setit)) {
                        yaoshi_pailie[i]=0;//钥匙盒中对应的位置置空
                        //tmpset.erase(setit++);
                        flag=true;
                        break;
                    }
                }
               
                setit++;//这个很重要
            }
            tmpset.clear();
        }
        it ++;         
    }
//输出结果
    for(int i=0;i<=Num_yaoshi-1;i++)
    cout<<yaoshi_pailie[i]<<" ";
//删除数组
   yaoshi_pailie=NULL;
   delete [] yaoshi_pailie;
   delete [] keyStatus;
   delete [] yaoshibianhao;
   delete [] starttime;
   delete [] timelong;
   delete [] endtime;
    return 0;
}

(2)使用一大堆的set来处理钥匙盒的问题,由于set是自排序的,所以这里弄了几个set来表示已经借的钥匙的集合,本时刻要还的钥匙的集合,空位置的集合。然后按照时刻从1到max_time进行遍历,对于每一个时刻,先找本时刻要还的钥匙的集合,然后还钥匙,然后再借钥匙。

#include <iostream>
#include <string>
#include <stdio.h>
#include <set>
using namespace std;

int main(){
    int N,K,MAX_TIME=0;
    cin>>N>>K;
    int *borrowTime=new int[K],*backTime=new int[K],*keyID=new int[K], *keys=new int[N];
    set<int> emptyPos;//used to record the postion that is empty, items in set are sorted by default
    set<int> borrowedKeys;//used to record the keys not returned
    set<int> returningKeys;//used to record the keys to returned at current time
    set<int> borrowingKeys;
    
    for(int i=0;i<N;i++){
        keys[i]=i+1;//initializing the kyes postion at the beginning
    }
    
    //get the status of the keys of the day
    for(int i=0;i<K;i++){
        scanf("%d %d %d\n",&keyID[i],&borrowTime[i],&backTime[i]);//>>endl;
        backTime[i]=backTime[i]+borrowTime[i];
        if(backTime[i]>MAX_TIME) MAX_TIME=backTime[i];
    }
    
    for(int i=1;i<=MAX_TIME;i++){
        int backAmount=0,borrowAcount=0;
        for(int j=0;j<K;j++){
            if(backTime[j]==i){//the key will be back at this period
                returningKeys.insert(keyID[j]);//put all the keys will be back at this period into the set
                borrowedKeys.erase(keyID[j]);
                backAmount++;
            }
            if(borrowTime[j]==i){//get the keys being borrowed at this period
                borrowingKeys.insert(keyID[j]);
                borrowedKeys.insert(keyID[j]);
                borrowAcount++;
            }
        }
        //put the sorted keys into the position specified by the sorted emptyPos 
        if(backAmount>0){
            for(int k=0;k<backAmount;k++){
                int key=*returningKeys.begin();
                int pos =*emptyPos.begin();
                keys[pos]=key;
                returningKeys.erase(key);
                emptyPos.erase(pos);
            }
        }
        //remove the borrowing keys at this period
        if(borrowAcount>0){
            for(int k=0;k<borrowAcount;k++){// can be replaced with map 
                for(int k1=0;k1<N;k1++){
                    int keyid=*borrowingKeys.begin();
                    if(keys[k1]==keyid){
                        emptyPos.insert(k1);
                        borrowingKeys.erase(keyid);
                        keys[k1]=0;
                        break;
                    }
                }
            }
        }
        borrowingKeys.clear();
        returningKeys.clear();
    }
    
    for(int s1=0;s1<N;s1++){
        printf("%d ",keys[s1]);
    }
    
    delete [] borrowTime;
    delete [] backTime;
    delete [] keys;
    delete [] keyID;
    return 0;
}?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值