pat(二)

1011——1020

1011[数学计算]

World Cup Betting

题目要求:数学计算而已

思路:整个数学公式

//
// @author prime on 2017/6/4.
//
#include <iostream>
using namespace std;

int main()
{
    float res[3];
    char choice[3];
    float a,b,c;
    for(int i=0;i<3;i++)
    {
        scanf("%f %f %f",&a,&b,&c);
        if(a>=b&&a>=c)
        {
            res[i]=a;
            choice[i]='W';
        } else if(b>=a&&b>=c)
        {
            res[i]=b;
            choice[i]='T';
        } else if(c>=a&&c>=b)
        {
            res[i]=c;
            choice[i]='L';
        }
    }
    double profit=(res[0]*res[1]*res[2]*0.65-1)*2;
    printf("%c %c %c %.2f",choice[0],choice[1],choice[2],profit);
    return 0;
}

1012[排序]

The Best Rank

题目要求:对成绩进行排序,输出每个人最好的排名。

思路:排序

这道题最好用一个数组保存每个人的最好成绩,这样空间换时间不容易出错。注意并列的情况,如果两个第一名成绩一样的话,就没有第二名了,直接下一个排第三。也就是说1 1 3 4 5而不是11 2 3 4这种布局。

排序用的标准库算法,方便的很,提前说一下,这个排序函数可以包含很多语意,后面的题目会遇到。

这里用循环的方法,每次针对一门课进行排序,记录相应的rank。注意最后的map查找操作,算法题最好用find

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

#include <iostream>
#include <algorithm>
#include <map>

using namespace std;


class Student
{
public:
    int id;
    int score[4];//0下标代表平均分
    int best;//最好的科目下标
    int rank[4];
};

char course[4]={'A','C','M','E'};
int current;//不能声明成index,我就fuck了
bool cmp(Student s1,Student s2)
{//根据不同情况进行不同规则的排序。
    return s1.score[current]>s2.score[current];
}

int main()
{
    map<int,int> exist;
    int n,m;
    scanf("%d %d",&n,&m);
    Student s[n];
    for (int i=0;i<n;++i)
    {
        scanf("%d %d %d %d",&s[i].id,&s[i].score[1],&s[i].score[2],&s[i].score[3]);
        s[i].score[0]=(s[i].score[1]+s[i].score[2]+s[i].score[3])/3.0+0.5;//平均数四舍五入
    }
    for(current=0;current<4;++current)
    {//分别按照每门课进行排名
        sort(s,s+n,cmp);
        s[0].rank[current]=1;
        for(int i=1;i<n;++i)
        {
            s[i].rank[current]=i+1;
            if(s[i].score[current]==s[i-1].score[current])
                s[i].rank[current]=s[i-1].rank[current];
        }
    }
    for(int i=0;i<n;++i)
    {
        exist[s[i].id]=i;//记录存在的id和其下标的映射
        int minx=s[i].rank[0];//记录最好科目的排名
        s[i].best=0;//记录最好科目的下标
        for(int j=1;j<4;j++)
        {
            if(s[i].rank[j]<minx)
            {
                minx=s[i].rank[j];
                s[i].best=j;
            }
        }
    }
    for(int i=0;i<m;++i)
    {
        int id,k;
        scanf("%d",&id);
        if(exist.find(id)!=exist.end())//map如果没有对应的键值,就会插入键,然后值初始化。用at会抛出异常,算法题不适合。还是用这种方法吧。
        {
            k=exist[id];
            printf("%d %c\n",s[k].rank[s[k].best],course[s[k].best]);

        } else
        {
            printf("N/A\n");
        }
    }
    return 0;
}

1013[连通分量+DFS]

Battle Over Cities

题目要求:求最少添加几条边让这个图连通。

思路:求出图的连通分量个数即可,添加连通分量数-1条边。因为当a个互相分立的连通分量需要变为连通图的时候,只需要添加a-1个路线,就能让他们相连。

对于每一个被占领的城市,去除这个城市结点,就是把它标记为已经访问过,这样在深度优先遍历的时候,对于所有未访问的结点进行遍历,就能求到所有的连通分量的个数

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

#include <iostream>
#include <algorithm>

using namespace std;

int G[1000][1000];
bool visited[1000];
int n;

void dfs(int source)
{
    visited[source]= true;
    for (int v=1;v<=n;++v)
    {
        if (!visited[v]&&G[source][v]==1)
        {
            dfs(v);
        }
    }
}

int main()
{
    int m,k;
    scanf("%d %d %d",&n,&m,&k);
    fill(visited,visited+n+1, false);
    for(int i=0;i<m;++i)
    {
        int u,v;
        scanf("%d %d",&u,&v);
        G[u][v]=G[v][u]=1;
    }
    for (int i=0;i<k;++i)
    {
        int v,res=0;
        scanf("%d",&v);
        visited[v]= true;
        for (int j=1;j<=n;++j)
        {
            if(!visited[j])
            {
                dfs(j);
                res++;//每当DFS一次,连通分量数加一。
            }
        }
        fill(visited,visited+n+1, false);
        printf("%d\n",res-1);
    }
    return 0;
}

1014[队列]

Waiting in Line

思路:对每个窗口设置一个结构体,两个成员记录第一个结束的时间和队列全部完成的时间,之所以记录第一个,是为了确定黄线外的人应该新加入哪条队列 ,而第二个是为了确定新加入的人是否sorry。注意,为了方便对时间统一处理,这里统一化成了分钟,这是很有用的思路。适当用goto语句也挺不错。

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

#include <iostream>
#include <deque>
#include <vector>

using namespace std;

struct Window
{
    deque<int> q;
    int first_over;//队列第一个结束的时间
    int last_over;//队列最后一个结束的时间
};

int main()
{
    int N,M,K,Q;
    scanf("%d %d %d %d",&N,&M,&K,&Q);
    vector<int> time(K+1);
    vector<bool> sorry(K+1, false);
    int index=1;//处理到第几个顾客
    vector<int> res(K+1);//记录每个顾客的结束时间
    for(int i=1;i<=K;++i)
    {
        scanf("%d",&time[i]);
    }
    vector<Window> w(N+1);

    for (int i=1;i<=M;++i)//队列中每一个
    {
        for (int j=1;j<=N;++j)//每个窗口
        {
            if(index<=K)
            {
                if(w[j].last_over>=540)//思维漏洞,没考虑黄线内的可能也会sorry
                {
                    sorry[index]= true;
                    goto xixi;
                }
                w[j].q.push_back(time[index]);
                w[j].last_over+=time[index];
                if(i==1)
                {//每队的第一个结束时间
                    w[j].first_over=time[index];
                }
                res[index]=w[j].last_over;//每次都是从最后开始排队,队伍结束就是自己结束
                xixi:
                    index++;
            }
        }
    }
    while(index<=K)
    {//有人在黄线外
        int min_time=w[1].first_over;
        int point=1;//记录下标
        for (int i=2;i<=N;++i)
        {
            if(w[i].first_over<min_time)
            {
                min_time=w[i].first_over;
                point=i;
            }
        }
        if(w[point].last_over>=540)
        {
            sorry[index]= true;
            goto hehe;
        }
        w[point].q.pop_front();
        w[point].q.push_back(time[index]);
        w[point].first_over+=w[point].q[0];
        w[point].last_over+=time[index];
        res[index]=w[point].last_over;
        hehe:
            ++index;
    }
    for (int i=1,check;i<=Q;++i)
    {
        scanf("%d",&check);
        if(sorry[check])
            printf("Sorry\n");
        else
        {
            int hour,minute;
            minute=res[check]%60;
            hour=res[check]/60;
            printf("%02d:%02d\n",hour+8,minute);
        }
    }
    return 0;
}

1015[素数+进制转换]

Reversible Primes

思路:很简单的思路,就是把各位反转后判断是否是素数即可。注意,如果原本就不是素数,直接输出否即可。

这里有两种思路,第一种是用对数函数判断位数,然后指数公式求解反转各位后的值。第二种是把各位放到数组中,然后累乘求出反转后的值。判断素数的函数也是比较经典的一种,背下来吧。

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

#include <iostream>
#include <cmath>

using namespace std;

bool isprime(int num)
{
    if(num<=1)//多加一个没坏处
        return false;
    for (int i=2;i<=sqrt(num);++i)
    {
        if(num%i==0)
            return false;
    }
    return true;
}


int main()
{
    int n,radix,reverse=0,digit;
    while(scanf("%d",&n)!=EOF)
    {
        if(n<0)
            break;
        scanf("%d",&radix);
        if(!isprime(n))
        {
            printf("No\n");
            continue;
        }
        digit=log10(n)/log10(radix)+1;//这里不能用log10(n),因为基数是radix,用换底公式解决吧!
        reverse=0;
        for (int i=digit-1;i>=0;--i)
        {
            reverse+=(int)pow((double)radix,(double)i)*(n%radix);
            n/=radix;
        }
        /*int len = 0;
        int arr[100];
        do{
            arr[len++] = n % radix;
            n = n / radix;
        }while(n != 0);
        for(int i = 0; i < len; i++) {
            n = n * radix + arr[i];
        }*/
        if(isprime(reverse))
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

1016[排序]

Phone Bills

思路:典型的排序问题。注意这里的排序函数的书写,后面很多题也用到这种形式。针对时间的计算依旧同一单位!

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


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

using namespace std;

struct Recode
{
    string name;
    int month,day,hour,minute,time;
    string status;//on or off?
};

bool compare(Recode & s1,Recode &s2)
{//一条比较语句可以包含很多信息
    return s1.name!=s2.name?s1.name<s2.name:s1.time<s2.time;
}


double calculate(const Recode &s,int const *cost, double a_day)
{
    double total=0;
    total+=cost[s.hour]*s.minute;
    for (int i=0;i<s.hour;i++)
        total+=60*cost[i];
    return (total+s.day*a_day)/100.0;
}

int main()
{
    int cost[24];
    for (int i=0;i<24;++i)
    {
        scanf("%d",&cost[i]);
    }
    int N;
    scanf("%d",&N);
    vector<Recode> input(N);//必须先预先分配足够的空间
    for(int i=0;i<N;++i)
    {
        cin>>input[i].name;
        scanf("%d:%d:%d:%d",&input[i].month,&input[i].day,&input[i].hour,&input[i].minute);
        input[i].time=input[i].day*24*60+input[i].hour*60+input[i].minute;
        cin>>input[i].status;
    }
    sort(input.begin(),input.end(),compare);
    map<string,vector<Recode>> res;
    vector<Recode>::iterator it=input.begin()+1,pre_it=input.begin();
    while(it!=input.end())
    {
        if(pre_it->name==it->name&&pre_it->status=="on-line"&&it->status=="off-line")
        {
            res[it->name].push_back(*pre_it);
            res[it->name].push_back(*it);
        }
        it++;
        pre_it++;
    }
    double a_day=0;//一天的消费
    for (int i=0;i<24;i++)
    {
        a_day+=cost[i]*60;
    }
    for (auto& e:res)
    {
        vector<Recode> tmp=e.second;
        cout<<e.first;
        printf(" %02d\n",tmp[0].month);
        double total=0;
        for (int i=0;i<tmp.size();i+=2)
        {
            printf("%02d:%02d:%02d %02d:%02d:%02d ",tmp[i].day,tmp[i].hour,tmp[i].minute,tmp[i+1].day,tmp[i+1].hour,tmp[i+1].minute);
            printf("%d ",tmp[i+1].time-tmp[i].time);
            double co=calculate(tmp[i+1],cost,a_day)-calculate(tmp[i],cost,a_day);
            printf("$%.2f\n",co);
            total+=co;
        }
        printf("Total amount: $%.2f\n",total);
    }
    return 0;
}

1017[排序+队列]

Queueing at Bank

思路:

本题是对现实的模拟。首先,先对所有人按到达时间进行排序(和往常一样,时间全部弄成秒)。对每个窗口,要记录其结束时间,这是为了确定黄线外队伍第一个人到哪个窗口进行排队。每当进入排队时,窗口结束时间就增加;如果来的时间晚于窗口结束时间,说明不需要等待,直接服务,窗口结束时间就是来的时间+服务时间。题目要求的是等待时间,不算服务时间的!!!

//
// @author prime on 2017/6/6.
//
#include <iostream>
#include <algorithm>
using namespace std;

struct Customer
{
    int arrive;//到达时间
    int take;//服务所需时间
};
bool cmp(const Customer& s1,const Customer &s2)
{
    return s1.arrive<s2.arrive;
}

int main()
{
    int N,K;//N个顾客,K个窗口
    scanf("%d %d",&N,&K);
    vector<Customer> c;
    for (int i=0;i<N;++i)
    {
        int hh,mm,ss,tt;
        scanf("%d:%d:%d %d",&hh,&mm,&ss,&tt);
        int s=hh*3600+mm*60+ss;
        if(s>61200)//5点之后来的不被服务
            continue;
        Customer tmp;
        tmp.arrive=s;
        tmp.take=tt*60;
        c.push_back(tmp);
    }
    if(c.size()==0)
    {
        printf("0.0");
        return 0;
    }
    int window[K]={28800};//每个窗口的结束时间 fuck,这个只初始化第一个元素。!!!!!!
    fill(window,window+K,28800);
    sort(c.begin(),c.end(),cmp);
    double total_time=0.0;
    for(int index=0;index<c.size();++index)
    {
        int fast=window[0],point=0;
        for (int i=1;i<K;++i)
        {
            if(window[i]<fast)
            {
                fast=window[i];
                point=i;
            }
        }
      //point指向最快的那个窗口
        if(window[point]<=c[index].arrive)//窗口结束时间早于顾客来,不用等待
        {
            window[point]=c[index].arrive+c[index].take;
        } else
        {
            total_time+=(window[point]-c[index].arrive);//需要等待
            window[point]+=c[index].take;//窗口结束时间增加
        }
    }
    printf("%.1f",total_time/60.0/c.size());
    return 0;
}

1018[Dijkstra+DFS]

Public Bike Management

思路:

首先进行Dijkstra算法求最短路径,在进行的过程中记录每个结点最近的前驱结点,距离一样就都记录下来。然后进行DFS,从终点开始,边递归边记录路径,到达起点之时,从起点开始沿着路径计算需要带过去多少自行车,带回来多少,然后进行全局变量值更新即可。

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

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

using namespace std;

const int inf=99999999;

int G[510][510];
int dist[510];
bool visit[510];
vector<int> pre[510];
int current_capacity[510];
int min_take=inf,min_back=inf;
vector<int> path,tmp_path;
int C,N,Sp,M;

void DFS(int v);

int main()
{
    scanf("%d %d %d %d",&C,&N,&Sp,&M);
    fill(G[0],G[0]+(500+10)*(500+10),inf);
    fill(dist,dist+510,inf);
    fill(visit,visit+510, false);
    dist[0]=0;
    for (int i=1;i<=N;++i)
    {
        scanf("%d",&current_capacity[i]);
    }
    for (int i=0;i<M;++i)
    {
        int u,v,cost;
        scanf("%d %d %d",&u,&v,&cost);
        G[u][v]=cost;
        G[v][u]=cost;
    }
    /*dijkstra*/
    for(int i=0;i<=N;++i)
    {
        int min_dist=inf,u=-1;
        for (int j=0;j<=N;++j)
        {
            if(!visit[j]&&dist[j]<min_dist)
            {
                u=j;
                min_dist=dist[j];
            }
        }
        if(u==-1)
            break;
        visit[u]=true;

        for (int v=0;v<=N;++v)
        {
            if(!visit[v]&&G[u][v]!=inf)
            {
                if(dist[u]+G[u][v]<dist[v])
                {
                    dist[v]=dist[u]+G[u][v];
                    pre[v].clear();
                    pre[v].push_back(u);//记录前一个结点
                } else if(dist[u]+G[u][v]==dist[v])
                {
                    pre[v].push_back(u);
                }
            }
        }
    }
    DFS(Sp);//从终点倒着DFS
    printf("%d 0",min_take);
    for (int i=path.size()-2;i>=0;i--)
    {
        printf("->%d",path[i]);
    }
    printf(" %d",min_back);
    return 0;
}


void DFS(int v)
{//tmp_path[0]是终点,DFS的过程是从终点到起点。。
    tmp_path.push_back(v);
    if(v==0)
    {//从起点到终点进行统计
        int take=0,back=0;//带来几辆车,带回几辆车?
        for (int i=tmp_path.size()-2;i>=0;--i)
        {//从离起点最近的点开始
            int id=tmp_path[i];
            if(current_capacity[id]>C/2)
            {
                back+=(current_capacity[id]-C/2);
            }
            else if(current_capacity[id]<C/2)
            {
                int odd=C/2-current_capacity[id];//缺几辆车?
                if(back<odd)
                {
                    take+=odd-back;
                    back=0;
                } else
                {
                    back-=odd;
                }
            }
        }
        if(take<min_take)
        {
            min_take=take;
            min_back=back;
            path=tmp_path;
        } else if(min_take==take&&back<min_back)
        {
            min_back=back;
            path=tmp_path;
        }
    }
    else
    {
        for (int i=0;i<pre[v].size();++i)
        {
            DFS(pre[v][i]);//v的前驱结点全部递归
        }

    }
    tmp_path.pop_back();
}



1019[进制转换+回文数]

General Palindromic Number

思路:

直接进制转换,然后对数组遍历即可。这里实现还是比较繁琐,最好用string,可以reverse之后看是否相等,就避免自己写判断,少写一点bug就越少啊。。

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

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    int N,b;
    scanf("%d %d",&N,&b);
    vector<int> res;
    if(N==0)
    {
        printf("Yes\n0");
        return 0;
    }
    while(N!=0)
    {
        res.push_back(N%b);
        N/=b;
    }
    vector<int>::iterator it1=res.begin(),it2=res.end()-1;
    int flag=true;
    while(it1<it2)
    {
        if(*(it1++)!=*(it2--))
        {
            flag=false;
            break;
        }
    }
    if(flag)
    {
        printf("Yes\n");

    } else
    {
        printf("No\n");
    }
    for (int i=res.size()-1;i>0;i--)
    {
        printf("%d ",res[i]);
    }
    printf("%d",res[0]);
    return 0;
}

1020[已知两个遍历序列求另一个]

Tree Traversals

思路:

给了后序和中序,可以根据它们重建二叉树,在数据结构复习一文中已经总结过。但是,当我傻傻的重新建立二叉树再遍历之后,才知道,原来可以不用建立树直接得出结果!too young too simple

笨办法:

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

#include <iostream>
#include <deque>

/*特殊用例 111*/
using namespace std;

struct Node
{
    int value;
    Node *left= nullptr;
    Node *right= nullptr;
    Node(int v)
    {
        value=v;
    }
};
Node *Build_tree(int *in_order,int *post_order,int length);

int main()
{
    int N;
    scanf("%d",&N);
    int post_order[N],in_order[N];
    for (int i=0;i<N;++i)
    {
        scanf("%d",&post_order[i]);
    }
    for (int i=0;i<N;++i)
        scanf("%d",&in_order[i]);
    Node *root=Build_tree(in_order,post_order,N);
    /*if(root)
        printf("%d",root->value);*/
    deque<Node> queue;
    Node *p=root;
    queue.push_back(*p);
    while(true)
    {

        *p=queue[0];
        if(p->left)
            queue.push_back(*p->left);
        if(p->right)
            queue.push_back(*p->right);
        if(queue.size()==1)
        {
            printf("%d",queue[0].value);
            return 0;
        }
        queue.pop_front();
        printf("%d ",p->value);
    }
}

Node *Build_tree(int in_order[],int post_order[],int length)//length代表中序序列长度
{
    if(length<=0)
    {
        return nullptr;
    }
    Node *root=new Node(post_order[length-1]);
    for(int i=0;i<length;++i)
    {
        if(in_order[i]==post_order[length-1])
        {
            int left_length=i,right_length=length-i-1;
            root->left=Build_tree(in_order,post_order,left_length);
            root->right=Build_tree(in_order+left_length+1,post_order+left_length,right_length);
            break;
        }
    }
    return root;
}

机智法:

首先先看已知后序和中序求先序:就和已经总结的二叉树知识一样,就是分割序列!

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

#include <iostream>

using namespace std;

int post[] = {3, 4, 2, 6, 5, 1};
int in[] = {3, 2, 4, 1, 6, 5};
int pre[]={1,2,3,4,5,6};

void preorder(int root,int start,int end)
{//root是后序遍历的下标,代表根节点。start和end代表中序序列的边界。
    if(start>end)
        return;
    int i=start;
    while(i<end&&post[root]!=in[i])
        i++;//i是中序序列中根的下标
    cout<<in[i]<<" ";
    preorder(root-(end-i+1),start,i-1);
    preorder(root-1,i+1,end);
}

void postorder(int root,int start,int end)
{
    if(start>end)
        return;
    int i=start;
    while(i<end&&in[i]!=pre[root]) ++i;
    postorder(root+1,start,i-1);
    postorder(root+(i-start)+1,i+1,end);
    cout<<in[i]<<" ";
}


int main()
{
    //preorder(5,0,5);
    postorder(0,0,5);
    return 0;
}

下面就是本题了,只需要简单添加一个数组level和一个变量index,数组一开始初始化-1,然后在上述求中序序列的过程中,index表示当前根节点的下标(从1开始,在level中记录),最后从头到尾遍历level数组,把非-1的值输出即可!——其实就是用线性结构存二叉树。2*index表示左孩子 ,2*index+1表示右孩子,同样,这里也有线性结构保存二叉树的通病,level必须很大!!!

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

#include <iostream>
#include <algorithm>

using namespace std;

int post[30];
int in[30];
int level[10000];

void pre_order(int root,int start,int end,int index)
{
    if(start>end)
        return;
    int i=start;
    while(i<end&&post[root]!=in[i]) i++;
    level[index]=post[root];
    pre_order(root-(end-i+1),start,i-1,2*index);
    pre_order(root-1,i+1,end,2*index+1);
}

int main()
{
    fill(level,level+10000,-1);
    int N;
    scanf("%d",&N);
    for (int i=0;i<N;++i)
        scanf("%d",&post[i]);
    for (int i=0;i<N;++i)
        scanf("%d",&in[i]);
    pre_order(N-1,0,N-1,1);

    printf("%d",level[1]);
    int count=1;
    for (int i=2;i<10000;i++)
    {
        if(count==N)
            break;
        if(level[i]!=-1)
        {
            printf(" %d",level[i]);
            count++;
        }
    }
    return 0;
}

算是一种奇淫技巧吧~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值