pat(四)

1031~1040

1030[Dijksra+DFS]

Travel Plan

思路:

又是喜闻乐见的图论,呵呵。和前面自行车问题一样(MD记住你了),先Dijkstra求出最短路径,在此过程中求出每个结点的前驱结点。然后DFS,从终点回到起点(DFS的边界就是到达起点了,这时候由起点到终点的唯一路径已经确定,计算费用即可),看是否是最低消费。如果是就更新全局变量~~~

这里的DFS不需要visited数组了,因为由于实际意义的限制,DFS方向一直会向前的,不会重新遍历已经遍历过的结点!!!

//
// @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();
    }
}

1031[字符串处理]

Hello World for U

思路:

仔细读题,确定n1,n2,n3之间关系即可,然后创建个二维数组保存输出,填写之即可。

这里n~1~=n~2~,且满足n~1~+n~2~+n~3~-2=N,并且n~2~在[3,N]之间,求n~1~和n~2~的最大值。

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

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    string input;
    cin>>input;
    int N=input.size();
    int n1=0,n3;
    for (int n2=3;n2<=N;n2++)
    {
        if((N-n2+2)%2==0&&(N-n2+2)/2>n1&&(N-n2+2)/2<=n2)
            n1=(N-n2+2)/2;
    }
    n3=n1;
    int n2=N+2-n1-n3;
    char out[n1][n2];
    memset(out,' ', sizeof(out));
    for (int i=0;i<n1;i++)
    {
        out[i][0]=input[i];
    }
    for (int i=0,j=n1-1;i<n2;++i,j++)
        out[n1-1][i]=input[j];
    for (int i=0,j=N-1;i<n3;++i,--j)
    {
        out[i][n2-1]=input[j];
    }

    for (int i=0;i<n1;++i)
    {
        for (int j=0;j<n2;j++)
            cout<<out[i][j];
        cout<<endl;
    }
    return 0;
}

1032[找链表公共结点]

Sharing

思路:

首先沿着一条链遍历,把这条路径上的结点记录到hash表中,然后从另一条链遍历,找到在hash表中的结点就输出并退出。

这个题关键在于hash表怎么建立?用set?不行 !最后一个用例太大,用set这个基于红黑树的结构还是太慢,会超时!用unorder_set?想法不错,这是c++11引入的,但是支持似乎还不够完善。还是用数组吧,手动hash!

另外说明一下,set的比较规则,其模板参数中有比较器这一参数,默认less<T>即调用T类型的<运算符进行比较。同理也有其它内置的仿函数。需要明确的是,set这个集合的等于定义为:假设a==b,则意味(!a

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

#include <iostream>

using namespace std;

struct Node
{
    char val;
    int next;
    bool flag;
    Node()= default;
    Node(char v,int n)
    {
        flag= false;
        this->val=v;
        this->next=n;
    }
};

int main()
{
    Node s[100000];
    int N,start1,start2;
    scanf("%d%d%d",&start1,&start2,&N);
    for(int i=0;i<N;++i)
    {
        int addr,n;
        char v;
        scanf("%d %c %d",&addr,&v,&n);
        s[addr].next=n;
        s[addr].val=v;
    }
    for (int begin=start1;begin!=-1;begin=s[begin].next)
    {
        s[begin].flag= true;
    }
    for (int begin=start2;begin!=-1;begin=s[begin].next)
    {
        if(s[begin].flag)
        {
            printf("%05d",begin);
            return 0;
        }
    }
    printf("-1");
    return 0;
}

1033[贪心]

To Fill or Not to Fill

思路:

基于贪心的准则,首先判断第一个加油站是不是距离为0,不满足说明根本走不了,直接退出。如果满足了,那么采取如下策略:即假设每次在加满油的情况下,看所能到达的所有加油站,如果有个比当前加油站便宜的,那就只维持油量到那个加油站;如果都比这个加油站贵,那就不用多说了,直接加满,能用便宜的谁也不想用贵的不是?还有一个注意点,为了方便结束,添加一个加油站代表终点,距离为目的距离,油价为0.

就是这么简单的思路,想全码对也不容易啊,还是缺少训练。

““c++
//
// @author prime on 2017/6/17.
//

include

include

include

using namespace std;

struct Station
{
double dis;//距离起点距离
double per;//单位燃料价格
};

bool cmp(const Station& o1,const Station &o2)
{
return o1.dis

1034[DFS+连通分量]

Head of a Gang

思路:

和求图的连通分量思路很像,但是有点区别。题目要求找一个集群,其中边权和大于阈值k且包含2个以上成员(老大也算),即连通分量必须保证结点大于2且边权和大于k。所以在DFS后要进行判断是否满足条件。而对于DFS过程,首先用visted数组记录已被遍历过的点,然后判断当前结点是不是老大,之后就是DFS的标准流程了,只不过,每次内部DFS之前,要把边权置0,为了保证total不被重复计算!

注意点:

  • 输入的时候边输入权值边求点的权值
  • 题目说N是电话记录,包括两个结点,可能极端到1000个电话记录都不有人重复,所以结点至少2000
  • 人名是string,用map转化为int方便图的计算

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

using namespace std;

int G[2010][2010];
int weight[2010];
bool visited[2010];

map<string,int> si;//名字到编号的映射
map<int,string> is;//编号到名字的映射
map<string,int> res;//老大和小弟数的键值对
int number=1;//顶点的个数

void DFS(int u,int &member,int &head,int &total)
{
    visited[u]=true;
    member++;
    if(weight[u]>weight[head])
        head=u;
    for (int v=1;v<=number;++v)
    {
        if(G[u][v]>0)
        {
            total+=G[u][v];
            G[u][v]=G[v][u]=0;//计算之后把边权置0,防止重复计算total
            if(!visited[v])
            {
                DFS(v,member,head,total);
            }
        }
    }
}

int main()
{
    int N,K;
    scanf("%d%d",&N,&K);
    for (int i=0;i<N;++i)
    {
        string name1,name2;int time;
        cin>>name1>>name2>>time;
        if(si[name1]==0)
        {
            si[name1]=number;
            is[number]=name1;
            number++;
        }
        if(si[name2]==0)
        {
            si[name2]=number;
            is[number]=name2;
            number++;
        }
        G[si[name1]][si[name2]]+=time;
        G[si[name2]][si[name1]]+=time;
        weight[si[name1]]+=time;
        weight[si[name2]]+=time;
    }
    fill(visited,visited+2010,false);
    for (int u=1;u<=number;++u)
    {
        if(!visited[u])
        {
            int head=u,total=0,member=0;
            DFS(u,member,head,total);
            if(member>2&&total>K)
                res[is[head]]=member;
        }
    }
    cout<<res.size();
    for (auto &e:res)
    {
        cout<<"\n"<<e.first<<" "<<e.second;
    }
    return 0;
}

1035[简单字符串替换]

Password

简单的替换,细心就ok~~~比如输出格式

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

#include <iostream>


using namespace std;

bool transform(string& s)
{
    bool flag= false;
    auto it=s.begin();
    while(it!=s.end())
    {
        if(*it=='O')
        {
            *it='o';
            flag=true;
        }
        else if(*it=='1')
        {
            *it='@';
            flag=true;
        } else if(*it=='0')
        {
            *it='%';
            flag=true;
        } else if(*it=='l')
        {
            *it='L';
            flag=true;
        }
        it++;
    }
    return flag;
}

int main()
{
    int N;
    cin>>N;
    pair<string,string> s[N];
    for (int i=0;i<N;++i)
    {
        cin>>s[i].first>>s[i].second;
    }
    int res=0;
    bool modify[N];
    fill(modify,modify+N, false);
    for (int i=0;i<N;++i)
    {
        if(transform(s[i].second))
        {
            res++;
            modify[i]=true;
        }

    }
    if(res>0)
    {
        cout<<res;
        for(int i=0;i<N;++i)
        {
            if(modify[i])
                cout<<"\n"<<s[i].first<<" "<<s[i].second;
        }
    }
    else
    {
        if(N==1)
            cout<<"There is 1 account and no account is modified";
        else
            cout<<"There are "<<N<<" accounts and no account is modified";
    }
    return 0;
}

1036[排序/求极值]

Boys vs Girls

思路:

简单求极值,通过排序可以方便解决。但是,这种单一求极值的问题,完全不必要全部保存,就为了考pat而言,直接保存需要的信息即可!

注意题目要求字符不超过10个字符,所以申请11个字符空间。不然过不去!

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

struct Student
{
    char name[11];//只能写10个字符
    char gender;//性别
    char id[11];
    int grade;
};

bool cmp(const Student& o1,const Student& o2)
{
    return o1.grade>o2.grade;
}

int main()
{
    int N;
    scanf("%d",&N);
    vector<Student> F,M;
    for (int i=0;i<N;++i)
    {
        Student tmp;
        scanf("%s %c %s %d",tmp.name,&tmp.gender,tmp.id,&tmp.grade);
        if(tmp.gender=='F')
            F.push_back(tmp);
        else
            M.push_back(tmp);
    }
    sort(F.begin(),F.end(),cmp);
    sort(M.begin(),M.end(),cmp);
    if(F.size()>0&&M.size()>0)
    {
        printf("%s %s\n%s %s\n",F[0].name,F[0].id,M.back().name,M.back().id);
        printf("%d",F[0].grade-M.back().grade);
    } else if(F.size()>0)
    {
        printf("%s %s\n",F[0].name,F[0].id);
        printf("Absent\n");
        printf("NA");
    } else if(M.size()>0)
    {
        printf("Absent\n");
        printf("%s %s\n",M.back().name,M.back().id);
        printf("NA");
    }
    return 0;
}

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

#include <iostream>

using namespace std;

int main()
{
    string male,female;
    int m_score=101,f_score=-1;
    int N;
    scanf("%d",&N);
    for (int i=0;i<N;++i)
    {
        string name,sex,id;
        int score;
        cin>>name>>sex>>id>>score;
        if(sex=="F")
        {
            if(score>f_score)
            {
                f_score=score;
                female=name+" "+id;
            }
        }
        else
        {
            if(score<m_score)
            {
                m_score=score;
                male=name+" "+id;
            }
        }
    }
    if(f_score!=-1&&m_score!=101)
    {
        cout<<female<<"\n"<<male<<"\n"<<f_score-m_score;
    }
    else if(f_score!=-1)
    {
        cout<<female<<"\n";
        cout<<"Absent\n";
        cout<<"NA";
    }
    else if(m_score!=101)
    {
        cout<<"Absent\n"<<male<<"\nNA";
    }
    return 0;
}

1037[贪心]

Magic Coupon

题意就是两个序列,其中任意两个数相乘,要求结果相加最大。

思路:

先从小到大排序,然后从后向前遍历两个序列,都是正数就相乘加到结果中,否则结束循环;然后,从前向后遍历两个序列,如果都是负数,相乘加到结果中,否则结束循环。

一开始我想着从后往前,如果相乘小于0,就把指针都递减,这样就只有一个用例没过去~~~这个思路是错误的,只适用于序列长度相等的情况。因为如果两个序列长度不等就错解。

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

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


using namespace std;

int main()
{
    int NC,NP;
    scanf("%d",&NC);
    vector<int> coupon(NC);
    for (int i=0;i<NC;++i)
    {
        scanf("%d",&coupon[i]);
    }
    scanf("%d",&NP);
    vector<int> product(NP);
    for (int i=0;i<NP;++i)
        scanf("%d",&product[i]);
    sort(coupon.begin(),coupon.end());//升序
    sort(product.begin(),product.end());
    long res=0;
    for(auto c_it=coupon.rbegin(),p_it=product.rbegin();c_it!=coupon.rend()&&p_it!=product.rend()&&*c_it>0&&*p_it>0;c_it++,p_it++)
    {
        res+=(*c_it)*(*p_it);
    }
    for(auto c_it=coupon.begin(),p_it=product.begin();c_it!=coupon.end()&&p_it!=product.end()&&*c_it<0&&*p_it<0;c_it++,p_it++)
    {
        res+=(*c_it)*(*p_it);
    }
    printf("%ld",res);
    return 0;
}

1038[字符串+贪心]

Recover the Smallest Number

很经典的题目,要求给出几个数,求它们的最小的一种排列。

思路:

很自然联想到排序,那么是按照字典序排一下就ok了吗?显然不是!任何两个字符串a、b排列的依据应该是把a+b和b+a中较小的那一个放在前面,另一个放在后面,这样局部最优从而全局最优。

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

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

bool cmp(const string &o1, const string &o2)
{
    return o1+o2<o2+o1;
}

int main()
{
    int N;
    cin>>N;
    string s[N];
    for (int i=0;i<N;++i)
        cin>>s[i];
    sort(s,s+N,cmp);
    string res;
    for (int i=0;i<N;++i)
        res+=s[i];
    while(res.size()!=0&&res[0]=='0')//防止第一个元素是0
    {
        res.erase(res.begin());
    }
    if(res.size()==0)
        cout<<'0';
    else
        cout<<res;
    return 0;
}

1039[查找]

Course List for Student

思路:

和前面中的1022思路一样,都是针对输出建立映射关系,这里要求查询学生选课,那么键就是学生,值就是选的课。由此,正常思路如下

//
// @author prime on 2017/6/22.
//
#include <iostream>
//#include <map>
#include <set>
#include <unordered_map>
using namespace std;

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    int N,K;
    cin>>N>>K;
    unordered_map<string,set<int>> course;
    for (int i=0;i<K;++i)
    {
        int index,n;
        string name;
        cin>>index>>n;
        for (int j=0;j<n;++j)
        {
            cin>>name;
            course[name].insert(index);
        }
    }
    string query;
    for (int i=0;i<N;++i)
    {
        cin>>query;
        cout<<query<<" "<<course[query].size();
        for (auto &e:course[query])
            cout<<" "<<e;
        cout<<"\n";
    }
    return 0;
}

很遗憾,最后一个用例是过不去的。因为超时了。为此,有了下面这种手动hash的方法,虽然能百分百通过,不过考试时候还是谨慎点,别捡了芝麻丢了西瓜。

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

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

int my_hash(char *name)
{
    int code=0;
    for (int i=0;i<3;++i)
    {
        code=code*26+name[i]-'A';
    }
    code=code*10+name[3]-'0';
    return code;
}

const int max_num=26*26*26*10+10;
vector<int> s[max_num];

int main()
{
    int N,K;
    scanf("%d%d",&N,&K);
    char name[5];//永远记住多留一个存储\0,虽然本地可能正确,oj不这么认为!!!
    for (int i=0;i<K;++i)
    {
        int index,n;
        scanf("%d%d",&index,&n);
        for (int j=0;j<n;++j)
        {
            scanf("%s",name);
            s[my_hash(name)].push_back(index);
        }
    }
    for (int i=0;i<N;++i)
    {
        scanf("%s",name);
        int id=my_hash(name);//在hash表中的位置
        sort(s[id].begin(),s[id].end());
        printf("%s %lld",name,s[id].size());
        for (auto & e:s[id])
            printf(" %d",e);
        printf("\n");
    }
    return 0;
}

1040[DP]

Longest Symmetric String

这道题也是经典题目,求最长回文子串的长度。

思路:

能用现有的算法,千万不要自己造轮子,容易出错!这道题可以用已有的求最长公共子串的DP算法,即把该串翻转后求和原串的最长公共子串的长度。

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

using namespace std;

int main()
{
    string in;
    getline(cin,in);
    size_t len=in.size();
    int dp[len+1][len+1];
    fill(dp[0],dp[0]+(len+1)*(len+1), 0);
    string rev=in;
    reverse(rev.begin(),rev.end());
    int res=-1;
    for (int i=1;i<=len;++i)
    {
        for (int j=1;j<=len;++j)
        {
            if(in[j-1]==rev[i-1])
            {
                dp[i][j]=dp[i-1][j-1]+1;
                if(dp[i][j]>res)
                {
                    res=dp[i][j];
                }
            }
            else
                dp[i][j]=0;
        }
    }
    cout<<res;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值