Winter Camp I (下)

M题: Stones

Winter Camp I (下) 

 Winter Camp I (下)

代码: 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
int N;
struct stone
{
    int p,d;
};
stone pl;
struct cmp
{
    bool operator()(const stone a,const stone b)
    {
        if(a.p!=b.p)
            return a.p>b.p;
        else
            return a.d>b.d;
    }
};
int main()
{
    int T;
    priority_queue<stone,vector<stone>,cmp>q;
    cin>>T;
    while(T--)
    {
        scanf("%d",&N);
        for(int i=0;i<N;i++)
        {
            scanf("%d%d",&pl.p,&pl.d);
            q.push(pl);
        }
        int flag=1;
        while(!q.empty())
        {
            pl=q.top();
            q.pop();
            if(flag)
            {
                pl.p+=pl.d;//把奇数次遇到的石头的位置距离和投掷距离相加,然后再入队
                q.push(pl);
            }
            flag=!flag;
        }
        printf("%d\n",pl.p);
    }
    return 0;
}

 

从M题以后的都是STL相关的问题,M题是STL中对优先队列的考察

题意:在一条直线道路上有n个石头,向前走遇到一个数一个,如果遇到的是位置是第奇数个那就把这个石头往前扔距离stone.d, 如果是第偶数个,就跳过不管。若遇到位置相同的,优先看见那个投掷距离短的石子。问遇到的最后一个石头距离出发点的位置是多少。

思路:本题是对其题意在算法上进行一个模拟,构造一个结构体用来记录石子的初始位置和投掷的距离。用优先队列来存储这些信息,并且根据题意,要让那些位置距离小的,投掷距离近的石头权重高,所以还要涉及到自己重新构建优先的判定规则。当遇到奇数时,把投掷后的距离累加到位置信息上,作为一个新的石子入队,然后把当前石子的信息出队删除,反复进行,直到队空结束。 

知识点:优先队列的使用及优先级的设定

 Q题: Word Amalgamation

Winter Camp I (下) 

Winter Camp I (下) 

Winter Camp I (下) 

代码: 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
using namespace std;
int main()
{
    map<string,string>word;
    string str,tmp;
    while(cin>>str&&str!="XXXXXX")
    {
        tmp=str;
        sort(str.begin(),str.end());
        word.insert(pair<string,string>(tmp,str));
    }
    while(cin>>str)
    {
        int flag=1;
        if(str=="XXXXXX")
            break;
        sort(str.begin(),str.end());
        map<string,string>::iterator it;
        for(it=word.begin();it!=word.end();it++)
        {
            if(str==it->second)
            {
                cout<<it->first<<endl;
                flag=0;
            }
        }
        if(flag)
            cout<<"NOT A VALID WORD"<<endl;
        printf("******\n");
    }
    return 0;
}

 

题意:就是先输入一个字典然后在输入一些乱码的单词,找出字典中"对应"的单词并输出,如果有多个那就按字典排序输出,如果没有就输出一行NOT A VALID WORD 当一个单词搜索完后不论找没找到都要输出一行******表示该单词的搜索结束。

思路:本题在对字典输入后,就要用sort对字典中的每个单词进行排序,(默认排序从小到大即可),然后用map<string,string>来分别记录键值和对应的表示,这里的键值就是字典中原本的单词,对应的表示就是单词排序后的样子。添加到map里的方法是用Value.insert()函数。

之后再输入乱序的单词,这里的关键点就是如何比较的问题,其实思路也不难,就是把这个乱序的单词也用sort()函数排序,当map里的表示与输入后的排序相同,那么就输出map里的键值,否则就是 Not a vaild word.

知识点:这里用到的map函数,map<string,string>word:map的构造,键值和表示都是字符串型。word.insert()往map里插入数值。遍历所有map记录的数据要用到迭代器:

map<string,string>::iterator it,这里it是一个类似于指针的东西,调用其第一个或者第二个内容的方式为(*it).first或it->first,

用迭代器遍历整个map的语句是

for(it=word.begin();it!=word.end();i++).

O题: What Are You Talking About 

Winter Camp I (下) Winter Camp I (下)

代码: 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<string>
using namespace std;
int main()
{
    map<string,string>dic;
    string s1,s2;
    char s[500],aa[500],ss[100];
    int k=0;
    scanf("%s",ss);
    while(cin>>s1)
    {
        if(s1=="END")
            break;
        getchar();
        getline(cin,s2);
        dic[s2]=s1;
    }
    getchar();
    while(gets(s))
    {
       if(strcmp(s,"END")==0)
          break;
       if(strcmp(s,"START")==0)
          continue;
       for(int i=0;s[i]!='\0';i++)
       {
           if(islower(s[i]))
               aa[k++]=s[i];
           else
           {
               aa[k]='\0';
               if(dic.count(aa))
                 cout<<dic[aa];
               else
                 printf("%s",aa);
               printf("%c",s[i]);
               k=0;
           }
       }
       printf("\n");
    }
    return 0;
}

 

题意:就是让你把乱码符号翻译成正常的英文并输出。

思路:首先是用map构建字典,之后通过gets()函数来读取一段含空格和标点的句子,并通过islower()来判断是否是英文字符以及map类下的count函数来判断是否是满足在字典里的乱码,满足的话则按字典规则里的对应关系输出这个英文单词,否则直接把这个乱码或非英文字符输出即可.

知识点:第一次提交的时候,系统显示TLE,说明程序不够简洁超时了,之后又提交了4次,前3次仍然是TLE,最后一次是数组开小了,总共5次提交,第六次的时候终于A了。

简单说一下程序优化的地方,虽然优化后时间花费的时间仍然很长

第一处是把用cin与cout输入输出的,能改的都用scanf与printf函数代替了,毕竟C语言接近底层还是相对C++的输入输出来说要快的。

第二处我用的是STL的map类下的insert函数来添加数据,但这显然耗时,之后用map是关联数组的特性,改成数组赋值的方式,判断是否与字典里的乱码字符想匹配,之前用的是map的迭代器来对整个map进行遍历,之后改成使用map下的count函数来进行检测,总体上是更改了这么多,现在简单说一下这个题里的知识点。

这个题仍然是对STL中map的应用,偏向map的库函数的方法,

islower(a)函数:检查参数a是否为小写英文字母, 若参数a为小写英文字母,则返回TRUE,否则返回NULL(0),头文件是ctype.h

map.count(r),r是否能在键值中找到,可以则返回TRUE,不可以则返回FALSE。

gets()函数从标准输入设备读入字符串函数,可以无限读取,不会判断上限,以回车结束读取,所以要确保buffer的空间足够大,以便在执行操作时不会溢出

getline()函数是专门给string类型变量输入字符串的,不写结束符时,默认以回车结束,同时输入结束时回车会从输入流中取出并丢弃。

F题: Crashing Robots 

 Winter Camp I (下)

Winter Camp I (下) 

Winter Camp I (下) 

Winter Camp I (下) 

Winter Camp I (下) 

代码: 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int A,B;//A:length(EW),B:(NS).
int N,M;//N:the number of robots,M:the number of instructions
int maze[120][120];
int dirx[4]={-1,0,1,0};
int diry[4]={0,1,0,-1};
int xi,yi;//记录机器人的初始位置
char di;//初始转向
int num,re;//执行指令的机器人型号和重复次数
char act;
struct Robot
{
    int x;
    int y;
    int d;
}robot[105];
bool forward(int num,int re)
{
    int xf,yf,df;
    xf=robot[num].x;
    yf=robot[num].y;
    df=robot[num].d;
    maze[xf][yf]=0;
    for(int i=0;i<re;i++)
    {
        xf+=dirx[df];
        yf+=diry[df];
        if(xf<1||xf>A||yf<1||yf>B)
        {
            cout<<"Robot "<<num<<" crashes into the wall"<<endl;
            return false;
        }
        if(maze[xf][yf])
        {
            cout<<"Robot "<<num<<" crashes into robot "<<maze[xf][yf]<<endl;
            return false;
        }
    }
    robot[num].x=xf;
    robot[num].y=yf;
    maze[xf][yf]=num;
    return true;
}
bool action(int num,char act,int re)
{
    switch(act)
    {
        case'F':return forward(num,re);
        case'L':robot[num].d=(robot[num].d-re%4+4)%4;break;
        case'R':robot[num].d=(robot[num].d+re%4)%4;break;
    }
    return true;
}
int main()
{
    int K;
    bool flag;
    cin>>K;
    while(K--)
    {
       scanf("%d%d",&A,&B);
       scanf("%d%d",&N,&M);
       memset(maze,0,sizeof(maze));
       memset(robot,0,sizeof(robot));
       for(int i=1;i<=N;i++)
       {
           cin>>xi>>yi>>di;
           robot[i].x=xi;
           robot[i].y=yi;
           maze[xi][yi]=i;
           switch(di)
           {
               case 'W':robot[i].d=0;break;
               case 'N':robot[i].d=1;break;
               case 'E':robot[i].d=2;break;
               case 'S':robot[i].d=3;break;
               default:robot[i].d=-1;
           }
       }
       flag=true;
       for(int i=0;i<M;i++)
       {
         cin>>num>>act>>re;
         if(flag)
            flag=action(num,act,re);
       }
       if(flag)
         printf("OK\n");
    }
    return 0;
}

这题其实不难,但WA了许多次,原因在于数组下标写错,代码大小写问题,下次遇到搞不出来的时候去POJ找些数据测试超级好用,一下子该对AC了。 

哦,对了,cin在空格结束后系统会从缓存中取出空格弃掉,不错,适合整型和字符一起输入的时候。

 R题: Matrix multiplication

Winter Camp I (下) 

Winter Camp I (下) 

 代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
int a[806][806],b[806][806],c[806][806];
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                scanf("%d",&a[i][j]);
                a[i][j]%=3;
            }
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                scanf("%d",&b[i][j]);
                b[i][j]%=3;
            }
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(!a[i][j])
                    continue;
                for(int k=0;k<n;k++)
                    c[i][k]+=a[i][j]*b[j][k];
            }
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(j==n-1)
                    printf("%d\n",c[i][j]%3);
                else
                    printf("%d ",c[i][j]%3);
            }
        }
    }
    return 0;
}

本题是一道常规的矩阵乘法题,常见的思路使用两个二维数组存储每个矩阵里的值,然后再模拟矩阵运算的乘法。

下面的代码是用于模拟矩阵运算乘法的代码: 

for(int i=0;i<n;i++)

        {

            for(int j=0;j<n;j++)

            {

                if(!a[i][j])

                    continue;

                for(int k=0;k<n;k++)

                    c[i][k]+=a[i][j]*b[j][k];

            }

        }

因为这里要求结果还必须对3取模,所以我们的思路是在输入时对3取个模,在输出时对3取个模。输入时对3取个模的作用是在后面的矩阵乘法过程中把0项直接跳过,方便简化后面的运算。输出结果取模,是为防止之前取完模后,相加又多了3的倍数,所以要再次取模,防止出现与运算结果不符的情况。

N题: Demonstration 

 题目so long,来个题目链接:

http://codeforces.com/problemset/problem/191/B

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
ll a[100005],b[100005];
ll n,k,m,i,sum;
bool cmp(ll a,ll b)
{
    return a>b;
}
int main()
{
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    cin>>n>>k>>m;
    for(i=1;i<=n;i++)
    {
        cin>>a[i];
        b[i]=a[i];
    }
    sort(b+1,b+n,cmp);
    sum=0;
    for(int i=1;i<k;i++)
    {
        sum+=b[i];
    }
    for(int i=1;i<n;i++)
    {
        if(a[i]>=b[k])//地之前选择了
        {
            if(sum+b[k]>m)
            {
                cout<<i<<endl;
                return 0;
            }
        }
        else if(sum+a[i]>m)//广场之前没选
        {
            cout<<i<<endl;
            return 0;
        }
    }
    cout<<n<<endl;
    return 0;
}

 

这题是一个贪心算法的题目,但题意真的是好难读懂啊

题意:*有N块地,现在有人想要反对*,要举行示威,向*申请场地。

这N块第,按照里*中心的距离远近编号为1-N, 1号地点最近。

*总是把反对者安排到最后一块地点,但要找个理由

于是当反对者申请一块地的时候,*就安排一个长期活动占用那个地方。当然这是要钱的

最后一块地最差,也是*所希望的,所以*不花钱。

现在问反对者能得到到最好的地是那一块。

有几个限制条件,也是输入数据。

地的块数,*的钱,工作天数(也是反对者最多申请的次数)

后面输入是从市中心到郊区的公园*在那里办活动的钱数

思路:我们把前N-1块地排个序,然后去前k-1个数字,累加,这样能最大程度耗光*的钱,那么我们最后一天就能申请到好的地

然后我们从头开始找最好的地

分为两种情况:

一个是这块地在我们之前已经选过,这个时候判断一下sum+b【k】和m的关系就可以,即是判定花钱最多的地方能否突破*的预算,若能突破,那么则可以在最后一天申请最好的地方,只要调整一下申请的顺序即可

一个是这块地之前没选过,但sum+a[i]>m,只要大于*预算即可实现最好地段公园的申请。

知识点:sort()函数的三个参数

1)第一个是要排序的数组的起始地址。

2)第二个是结束的地址(最后一位要排序的地址的下一地址

3)第三个参数是排序的方法,可以是从大到小也可是从小到大,还可以不写第三个参数,此时默认的排序方法是从小到大排序。