pat(三)

1021——1030

1021[DFS+连通分量]

Deepest Root

题目大意就是如果图是连通的,输出最深的树的根结点的并集;否则输出错误信息。

思路:

先用DFS判断连通分量个数,如果是1个表示图连通,此时tmp也记录了所有最深的根结点,然后随便取一个(这里取了第一个)再次DFS,两次并起来就是结果。

//
// @author prime on 2017/6/8.
//
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
int N;
vector<vector<int>> G;//邻接表。不能用邻接矩阵因为太大了超内存
vector<int> tmp;//记录根节点的集合
set<int> res;
bool visited[10001];
int max_depth=-1;

void DFS(int u,int depth)
{
    visited[u]= true;
    if(depth>max_depth)
    {
        max_depth=depth;
        tmp.clear();
        tmp.push_back(u);
    } else if(depth==max_depth)
    {
        tmp.push_back(u);
    }
    for (int i=0;i<G[u].size();++i)
    {
        if(!visited[G[u][i]])
        {
            DFS(G[u][i],depth+1);
        }
    }
}
int main()
{
    fill(visited,visited+10001, false);
    scanf("%d",&N);
    G.resize(N+1);
    for (int i=0;i<N-1;++i)
    {
        int u,v;
        scanf("%d %d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    int cc=0;//连通分量个数
    int begin;//记录第一次DFS后tmp的第一个元素,后面以此为起点再次DFS。二次DFS并集就是所有的结果(一次可能会漏解)
    /*判断连通分量*/
    for (int i=1;i<=N;++i)
    {
        if(!visited[i])
        {
            DFS(i,1);
            cc++;
            if(i==1)
            {
                for (int j=0;j<tmp.size();++j)
                {
                    res.insert(tmp[j]);
                    if(j==0)
                        begin=tmp[j];
                }
            }
        }
    }
    if(cc>1)
        printf("Error: %d components",cc);
    else
    {
        fill(visited,visited+N+1, false);
        tmp.clear();
        //max_depth=0;
        DFS(begin,1);
        for (int i=0;i<tmp.size();++i)
        {
            res.insert(tmp[i]);
        }
        for (set<int>::iterator it=res.begin();it!=res.end();it++)
            printf("%d\n",*it);
    }
    return 0;
}

1022[查找]

Digital Library

思路:

程序目的是查询,即输入有关信息,输出相应信息。由此,这里依旧沿用用空间换时间的战术,对每个查询信息建立对应的键值~

//
// @author prime on 2017/6/8.
//
#include <iostream>
#include <map>
#include <set>

using namespace std;

map<string,set<int>> Title,Author,Keywords,Publisher,Year;

void query(const map<string,set<int>> &s,string &query_str);

int main()
{
    int N,M;
    cin>>N;
    string title,author,keywords,publisher,year;
    int id;
    for (int i=0;i<N;++i)
    {
        cin>>id;
        getchar();//读取换行符
        getline(cin,title);
        Title[title].insert(id);
        getline(cin,author);
        Author[author].insert(id);
        while (cin>>keywords)
        {
            Keywords[keywords].insert(id);
            char c=getchar();//读取字符,提升到int后返回,这样可以容纳EOF等特殊值
            if(c=='\n')
                break;
        }
        /*getline(cin,keywords);
        for (string & e:split(keywords,' '))
        {
            Keywords[e].insert(id);
        }*/
        getline(cin,publisher);
        Publisher[publisher].insert(id);
        getline(cin,year);
        Year[year].insert(id);
    }
    string query_str;
    int num;
    cin>>M;
    for (int i=0;i<M;++i)
    {
        scanf("%d: ",&num);
        getline(cin,query_str);
        cout<<num<<": "<<query_str<<"\n";
        if(num==1)
        {
            query(Title,query_str);
        } else if(num==2)
            query(Author,query_str);
        else if(num==3)
            query(Keywords,query_str);
        else if(num==4)
            query(Publisher,query_str);
        else if(num==5)
            query(Year,query_str);
    }
    return 0;
}

void query(const map<string,set<int>> &s,string &query_str)
{
    map<string,set<int>>::const_iterator it=s.find(query_str);//find返回迭代器,其指向一个pair,pair.first==key
    if(it==s.cend())
    {
        printf("Not Found\n");
    } else
    {
        for (auto &e:it->second)//set可以直接这样遍历,用迭代器也行。基于范围的for循环本质上可以运用于任何拥有begin和end成员的序列
            printf("%07d\n",e);//必须7位,我就艹了!
    }
}

1023[进制转换]

Have Fun with Numbers

思路:

就是把输入的数翻倍,之后看输入的每一位是否在输出中都出现了且出现次数一样。

//
// @author prime on 2017/6/11.
//
#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
    int set[10];
    fill(set,set+10,0);
    string num;
    cin>>num;
    int res[num.size()+1];
    fill(res,res+num.size()+1,0);
    for (int i=num.size()-1;i>=0;i--)
    {
        num[i]-='0';
        set[num[i]]++;
        num[i]*=2;

        res[i+1]+=num[i];

        if(num[i]>9)
        {
            res[i]+=num[i]/10;
            res[i+1]%=10;
        }
    }
    //cout<<"length="<<num.size()<<endl;
    for (int i=0;i<num.size()+1;++i)
    {
        if(i==0&&res[i]==0)
            continue;
        if(set[res[i]]>0)
            set[res[i]]--;
        else
        {
            printf("No\n");
            goto NO;
        }
    }
    printf("Yes\n");
    NO:
    for (int i=0;i<num.size()+1;++i)
    {
        if(i==0&&res[i]==0)
        {
            continue;
        }
        printf("%d",res[i]);
    }
    return 0;
}

1024[字符串]

Palindromic Number

题目是非常经典的回文数问题。

思路:解决这个问题的最快方法是用string,而不是用数组。因为有内建的reverse函数,很方便。

//
// @author prime on 2017/6/11.
//

#include <iostream>
#include <algorithm>

using namespace std;

void add(string &a,string &b)
{
    string res(a.size(),0);
    int carry=0;//是否进位
    for (int i=a.size()-1;i>=0;i--)
    {
        res[i]=a[i]+b[i]-'0'+carry;
        carry=0;
        if(res[i]>'9')
        {
            carry=1;
            res[i]-=10;
        }
    }
    if(carry)
        res='1'+res;
    a=res;
}

int main()
{
    int K;
    string in;
    cin>>in>>K;
    for (int i=0;i<K;++i)
    {
        string b=in;
        reverse(b.begin(),b.end());
        if(b==in)
        {
            cout<<b<<'\n'<<i;
            return 0;
        }
        add(in,b);
    }
    cout<<in<<"\n"<<K;
    return 0;
}

下面是数组版的,有一个用例过不去:

//
// @author prime on 2017/6/11.
//

#include <iostream>
#include <vector>

using namespace std;

bool is_palindromic(const vector<int> &a)
{
    bool flag=true;
    if(a.size()==1)
        return true;
    for (auto pre=a.cbegin(),it=a.cend()-1;pre!=it;pre++,it--)
    {
        if(*pre!=*it)
        {
            flag= false;
            break;
        }
    }
    return flag;
}

void add(vector<int> &a)
{
    auto it=a.begin(),re=a.end()-1;
    vector<int> tmp;
    while (it!=a.end())
    {
        tmp.push_back(*it+*re);
        it++;re--;
    }
    for (int i=tmp.size()-1;i>0;--i)
    {
        if(tmp[i]>9)
        {
            tmp[i-1]+=tmp[i]/10;
            tmp[i]%=10;
        }
    }
    if(tmp[0]>9)
    {
        tmp.insert(tmp.begin(),0);
        tmp[0]=tmp[1]/10;
        tmp[1]%=10;
    }
    a=tmp;
}

int main()
{
    string input;
    int k;
    cin>>input>>k;
    vector<int> a;
    for (auto & e:input)
    {
        a.push_back(e-'0');
    }
    int total=0;
    for (int i=0;i<k;i++)
    {
        if(is_palindromic(a))
            goto YES;
        add(a);
        total++;
    }
    for (auto &e:a)
        cout<<e;
    cout<<'\n'<<k;
    return 0;

    YES:
    for (auto &e:a)
        cout<<e;
    cout<<'\n'<<total;
    return 0;
}

1025[排序]

PAT Ranking

思路:类似的排序题前面已经做过不少了,这里也一样。先局部小排,再全局大排。

这种题不是很难,但要细心,什么时候可以合并步骤什么时候不行,得清楚。

//
// @author prime on 2017/6/11.
//

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

struct Testee
{
    string id;
    int local_num;
    int local_rank;
    int score;
    int total_rank;
};

bool cmp(const Testee& s1, const Testee &s2)
{
    return s1.score==s2.score?s1.id<s2.id:s1.score>s2.score;
}

int main()
{
    int N,K;
    cin>>N;
    vector<Testee> res;
    int total=0;
    for (int i=0;i<N;++i)
    {
        cin>>K;
        total+=K;
        Testee tmp;
        for (int j=0;j<K;++j)
        {
            cin>>tmp.id>>tmp.score;
            res.push_back(tmp);
        }
        /*局部排序*/
        sort(res.end()-K,res.end(),cmp);

        res[total-K].local_rank=1;
        res[total-K].local_num=i+1;

        for (int j=1;j<K;++j)
        {
            res[total-K+j].local_num=i+1;
            if(res[total-K+j].score==res[total-K+j-1].score)
            {
                res[total-K+j].local_rank=res[total-K+j-1].local_rank;
                continue;
            } else
            {
                res[total-K+j].local_rank=j+1;
            }
        }
    }
    sort(res.begin(),res.end(),cmp);
    cout<<total<<endl;
    res[0].total_rank=1;
    for (int i=1;i<res.size();i++)
    {//这一步不能省,起初想法直接输出排名,但是需要考虑可能有多于2个成绩一样的情况,遂两个用例过不去
        if(res[i].score==res[i-1].score)
        {
            res[i].total_rank=res[i-1].total_rank;
        }
        else
           res[i].total_rank=i+1;
    }
    for (auto &e:res)
    {
        cout<<e.id<<" "<<e.total_rank<<" "<<e.local_num<<" "<<e.local_rank<<endl;
    }
    return 0;
}

1026[模拟+排序]

Table Tennis

思路:参照以往银行排队问题,通过记录每个桌子最早结束时间来判断下一桌。时间依旧化成秒统一计算。主循环遍历人,根据下一桌和当前人是否vip分为四种情况:

  • 下一张桌子是vip,人也是。直接安排。
  • 下一张桌子是vip,人不是。对队伍进行遍历,看是否有vip且vip客户到达时间在该桌子空闲之前,根据情况安排vip或者当前人。
  • 下一张桌子不是vip,人是vip。先看vip桌子里是否有不需要等的,优先安排到vip桌。否则当做普通人安排即可。
  • 下一张桌子和人都不是vip,那就直接安排。
//
// @author prime on 2017/6/12.
//

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;


struct Table
{
    int over_time;//结束时间
    int serve_count;//服务人数
    int tag;//VIP桌?
};


struct People
{
    int arrive_time;
    int tag;
    int serve_time;//开始服务时间
    int play_time;//玩了多久
};

int N,K,M;
vector<People> p;
vector<Table> t;//K张桌子1~K


bool cmp(const People& o1,const People& o2)
{//根据到达时间进行排序
    return o1.arrive_time<o2.arrive_time;
}

bool cmp2(const People& o1,const People& o2)
{//根据服务开始时间进行排序
    return o1.serve_time<o2.serve_time;
}

int Find_next_vip(int vip)
{
    vip++;
    for (;vip!=p.size()&&p[vip].tag==0;vip++);
    return vip;
}

void Allocate_table(int current,int point)
{
    if(p[current].arrive_time<t[point].over_time)
    {
        p[current].serve_time=t[point].over_time;
    } else
    {
        p[current].serve_time=p[current].arrive_time;
    }
    t[point].over_time=p[current].play_time+p[current].serve_time;
    t[point].serve_count++;
}


int main()
{
    scanf("%d",&N);
    People tmp;
    for (int i=0,h,m,s,play;i<N;++i)
    {
        scanf("%d:%d:%d %d %d",&h,&m,&s,&play,&tmp.tag);
        tmp.arrive_time=h*3600+m*60+s;
        if(tmp.arrive_time>=21*3600)
            continue;
        tmp.serve_time=21*3600;//表示还没有被计算过
        tmp.play_time=play<120?play*60:7200;//最多2小时
        p.push_back(tmp);
    }
    scanf("%d %d",&K,&M);
    t.resize(K+1);
    for (auto & e:t)//桌子们初始化
    {
        e.over_time=8*3600;//一开始所有桌子的结束时间初始化8时
        e.serve_count=0;
        e.tag=0;
    }
    for (int i=1,num;i<=M;i++)
    {
        scanf("%d",&num);
        t[num].tag=1;
    }
    sort(p.begin(),p.end(),cmp);//按照来的顺序排队

    int current=0,vip_index=-1;//当前队列的索引,下一个vip的索引
    vip_index=Find_next_vip(vip_index);
    while(current<p.size())
    {//对队伍进行遍历
        int point=0,min_end_time=999999;
        for (int i=1;i<=K;++i)
        {//用point指向最快结束的一桌
            if(t[i].over_time<min_end_time)
            {
                min_end_time=t[i].over_time;
                point=i;
            }
        }
        if(min_end_time>=21*3600)//打烊了,不再接待新顾客
            break;
        if(p[current].tag==1&&current<vip_index)
        {//中间优先安排的要过滤掉
            current++;
            continue;
        }
        if(t[point].tag==1)
        {//下一个是vip桌
            if(p[current].tag==1)
            {//正好下一个人是VIP
                Allocate_table(current,point);
                vip_index=Find_next_vip(vip_index);
                current++;
            }
            else
            {
                if(vip_index<p.size()&&p[vip_index].arrive_time<=t[point].over_time)
                {//如果队伍后面还有vip则优先安排它到这个桌子
                    Allocate_table(vip_index,point);
                    vip_index=Find_next_vip(vip_index);
                } else
                {
                    Allocate_table(current,point);
                    current++;
                }
            }
        }
        else
        {//下一桌不是VIP桌
            if(p[current].tag==0)
            {//下一个人也不是VIP
                Allocate_table(current,point);
                current++;
            } else
            {//下一个人是VIP
                int vip_point=-1,min_vip_end_time=99999999;
                for (int i=1;i<=K;++i)
                {
                    if(t[i].tag==1&&t[i].over_time<min_vip_end_time)
                    {
                        vip_point=i;
                        min_vip_end_time=t[i].over_time;
                    }
                }//先在vip桌中找,找不到就当作普通客人安排
                if(vip_point!=-1&&p[current].arrive_time>=min_vip_end_time)
                    Allocate_table(current,vip_point);
                else
                    Allocate_table(current,point);

                vip_index=Find_next_vip(vip_index);//更新下一个vip
                current++;
            }
        }
    }
    sort(p.begin(),p.end(),cmp2);
    for (int i=0;i<p.size()&&p[i].serve_time<21*3600;i++)
    {
        printf("%02d:%02d:%02d ",p[i].arrive_time/3600,p[i].arrive_time%3600/60,p[i].arrive_time%60);
        printf("%02d:%02d:%02d ",p[i].serve_time/3600,p[i].serve_time%3600/60,p[i].serve_time%60);
        printf("%.0f\n",round((p[i].serve_time-p[i].arrive_time)/60.0));
    }
    for (int i=1;i<=K;++i)
    {
        printf("%d",t[i].serve_count);
        if(i!=K)
            printf(" ");
    }
    return 0;
}



1027[进制转换]

Colors in Mars

思路:简单的进制转换问题,难度不大。细心就好。

//
// @author prime on 2017/6/12.
//


#include <iostream>
#include <vector>

using namespace std;

char map[13]={'0','1','2','3','4','5','6','7','8','9','A','B','C'};

vector<int> convert(int a)
{
    vector<int> res;
    res.push_back(a/13);
    res.push_back(a%13);
    return res;
}

int main()
{
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    vector<int> A=convert(a),B=convert(b),C=convert(c);
    printf("#%c%c%c%c%c%c",map[A[0]],map[A[1]],map[B[0]],map[B[1]],map[C[0]],map[C[1]]);
    return 0;
}

1028[排序]

List Sorting

思路:就是简单的排序,不过,最后一个用例很大,用string保存name会超时的。本以为用cin慢,关闭同步、换scanf都不好使。换成char数组就过了 ,太坑了吧?

如果不是这里限制,一般id这种都限制几位,用string较为方便,用整型输出时一定记住格式!看是否需要补全。

//
// @author prime on 2017/6/13.
//

#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>

using namespace std;

struct Student
{
    int id;
    char name[10];
    int score;
};

bool cmp_id(const Student& o1, const Student& o2)
{
    return o1.id<o2.id;
}

bool cmp_name(const Student& o1,const Student&o2)
{
    //return o1.name!=o2.name?o1.name<o2.name:o1.id<o2.id;
    return strcmp(o1.name,o2.name)<=0;
}

bool cmp_score(const Student&o1,const Student&o2)
{
    //return o1.score!=o2.score?o1.score<o2.score:o1.id<o2.id;
    return o1.score<=o2.score;
}

//最后一个测试点超大,string会超时。。。

int main()
{
    int N,C;
    scanf("%d%d",&N,&C);
    vector<Student> s(N);
    for (int i=0;i<N;i++)
    {
        scanf("%d %s %d",&s[i].id,s[i].name,&s[i].score);
    }
    switch (C)
    {
        case 1:
            sort(s.begin(),s.end(),cmp_id);
            break;
        case 2:
            sort(s.begin(),s.end(),cmp_name);
            break;
        case 3:
            sort(s.begin(),s.end(),cmp_score);
            break;
    }
    for (int i=0;i<N;++i)
    {
        printf("%06d %s %d\n",s[i].id,s[i].name,s[i].score);
    }
    return 0;
}

1029[两个指针]

Median

思路:假设两个序列长度m,n,找到中位数就是去掉(m+n-1)/2个数字即可。所有就用两个指针,一直剃掉元素。这里的题目意思是上中位数

//
// @author prime on 2017/6/13.
//



#include <iostream>
#include <vector>

using namespace std;

int main()
{

    int n1,n2;
    scanf("%d",&n1);
    vector<long int> a(n1);
    for (int i=0;i<n1;++i)
        scanf("%ld",&a[i]);
    scanf("%d",&n2);
    vector<long int> b(n2);
    for (int i=0;i<n2;++i)
        scanf("%ld",&b[i]);

    auto p1=a.cbegin(),p2=b.cbegin();
    int count=(n1+n2-1)/2;//舍弃这么多的数字
    while(count!=0)
    {
        if(p1==a.cend())
        {
            p2++;
            goto next;
        }
        if(p2==b.cend())
        {
            p1++;
            goto next;
        }
        if(*p1<=*p2)
            p1++;
        else
            p2++;
        next:
        count--;
    }
    long res;
    if(p1!=a.cend()&&p2!=b.cend())
        res=*p1>*p2?*p2:*p1;
    else
        res=p1==a.cend()?*p2:*p1;
    printf("%ld",res);

    return 0;
}

1030[Dijkstra+DFS]

Travel Plan

又到了图论,好消息是PAT的图论题套路差不多,和之前的自行车调度问题很像;坏消息是模板我还是记不住/(ㄒoㄒ)/~~。

思路:

首先计算最短路径,对每个结点来说,记录离它最近的前驱结点,如果两个距离都一样是最近的,那么就都记录进去。然后从终点开始,逆向DFS(有点回溯的味道),主要针对所有的前驱结点,一边前进一边记录路径。直到到达起始结点后,对路径的所有的结点计算总费用,如果总费用小于全局变量min_cost就更新它,并把当前路径拷贝给path。(DFS时无需再用visited数组,因为到达起点就是结束)

//
// @author prime on 2017/6/15.
//

#include <iostream>
#include <vector>

using namespace std;

const int inf=99999999;
int G[510][510];
int cost[510][510];
int dist[510];
bool visited[510];
int N,M,S,D;
vector<int> pre[510];
vector<int> path,tmp_path;

int min_cost=inf,tmp_cost;
void DFS(int u);


int main()
{
    /*初始化各种变量*/
    fill(visited,visited+510, false);
    fill(G[0],G[0]+510*510,inf);
    fill(dist,dist+510,inf);
    scanf("%d%d%d%d",&N,&M,&S,&D);
    for (int i=0,u,v;i<M;++i)
    {
        scanf("%d %d",&u,&v);
        scanf("%d %d",&G[u][v],&cost[u][v]);
        G[v][u]=G[u][v];
        cost[v][u]=cost[u][v];
    }
    dist[S]=0;
    pre[S].push_back(S);
    for (int i=0;i<N;++i)
    {
        int min_dist=inf,u=-1;
        for (int j=0;j<N;++j)
        {
            if(!visited[j]&&min_dist>dist[j])
            {
                min_dist=dist[j];
                u=j;
            }
        }
        if(u==-1)
            break;
        visited[u]=true;

        for (int v=0;v<N;++v)
        {
            if(!visited[v]&&G[u][v]!=inf)
            {
                if(dist[v]>dist[u]+G[u][v])
                {
                    pre[v].clear();
                    pre[v].push_back(u);
                    dist[v]=dist[u]+G[u][v];
                } else if(dist[v]==dist[u]+G[u][v])
                {
                    pre[v].push_back(u);
                }
            }
        }
    }
    //fill(visited,visited+N, false);
    DFS(D);
    for (int i=path.size()-1;i>=0;--i)
        printf("%d ",path[i]);
    printf("%d %d",dist[D],min_cost);
    return 0;
}

void DFS(int u)
{
    //visited[u]=true;
    if(u==S)
    {//从终点已经回到起点
        tmp_path.push_back(u);
        tmp_cost=0;
        for (int i=tmp_path.size()-1;i>0;i--)
        {//从源点到目的结点的路径
            int index=tmp_path[i];
            int next_index=tmp_path[i-1];
            tmp_cost+=cost[index][next_index];
        }
        if(tmp_cost<min_cost)
        {
            min_cost=tmp_cost;
            path=tmp_path;
        }
        tmp_path.pop_back();
    }
    else
    {
        tmp_path.push_back(u);
        for (int i=0;i<pre[u].size();i++)
        {
            DFS(pre[u][i]);
        }
        tmp_path.pop_back();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值