泰山挑夫解题报告

来源:互联网 发布:优化手机相机 编辑:程序博客网 时间:2024/06/10 18:54

A - Watermelon

判断一个数是否能分成两个偶数的和,这个数的范围是1--100;

枚举就好,奇数不能被分,偶数一般都能被分,除了2……



B - Before an Exam

还有n天时间复习,总共复习sum小时,而且每天复习时间还有上下限,给出一个合理复习计划,就是每天复习几个小时,能报任务完成!

因为数据不大,所以直接DFS,用ans数组保存下标,同时加回溯保证正确,加剪枝保证效率!

#include<iostream>#include<cstdio>#include<cstring>#include<map>#include<vector>using namespace std;map <string,int>mmap;int n,sum;int cnt1[35],cnt2[35];int num1[35],num2[35];int ans[35];bool flag;bool DFS(int day,int hour,int tot){   // cout<<day<<" "<<hour<<" "<<tot<<endl;   if(day==1&&hour==0)        {            flag=1;            return true;        }    if(day<1||hour<0||tot>n)return false;    if(day==1&&hour==0)return true;    for(int j=num1[tot]; j<=num2[tot]; ++j)    {        ans[tot]=j;        if(day==1&&hour-j==0)        {            flag=1;            return true;        }        if(hour-j>=cnt1[n]-cnt1[tot]&&hour-j<=cnt2[n]-cnt2[tot])        {            if(!DFS(day-1,hour-j,tot+1))            {                continue;            }else break;        }        else continue;    }}int main(){    memset(cnt1,0,sizeof(cnt1));    memset(cnt2,0,sizeof(cnt2));    memset(num2,0,sizeof(num2));    memset(num1,0,sizeof(num1));    memset(ans,0,sizeof(ans));    cin>>n>>sum;    flag=0;    for(int i=1; i<=n; ++i)    {        scanf("%d%d",&num1[i],&num2[i]);        cnt1[i]=cnt1[i-1]+num1[i];        cnt2[i]=cnt2[i-1]+num2[i];    }    //cout<<"num2 :"<<num2[2]<<endl;    DFS(n,sum,1);    if(flag)    {        printf("%s\n","YES");        for(int tt=1; tt<=n; ++tt)            printf("%d ",ans[tt]);//<<endl;            cout<<endl;    }else printf("%s\n","NO");//cout<<"NO"<<Endl;    return 0;}




C - Registration system

注册系统,新用户输入用户名,如果没被用过返回一个OK;反之,返回一个提示:name+i  name是用户自己提交的但是已经存在的!  要理解好题意!

i是从1开始没和name组合的最小的数!

这个题虽然一开始就想到map,但是我是用的把字符串加数字组合成一个新串插入到map中,然后再不断枚举数字知道找到一个合适的!  但是后来超时了,后来才想到可以和数字映射,直接记录个数,下次直接用这个数加1就好了!  改完之后不超时但是WA了,因为前期遇到的问题较多,做不下去了,开始怀疑题意了,是不是有坑,没敢再继续该下去,其实就只有一个小地方需要改动了,本来这个地方不用改,但是别的地方一改影响到这个地方了,所以……       

遇到的问题,map不会用,不会insert,对mm[str]=1的插入形式运用的不熟!

c++处理字符串很烂,掌握的太差,以至于输出时出现异常,我用printf输出string,当时一度以为是map出了问题,因为map确实出过好多问题……   基础知识掌握的太差了,这里就被卡主了,以至于我连try catch都用上了……  最后来时差一步……  但和别人的差距去不是一步……


#include<iostream>#include<cstdio>#include<cstring>#include<map>#include<vector>using namespace std;map <string,int>mmap;int n;int main(){    string str;    cin>>n;    while(n--)    {        cin>>str;        if(mmap.find(str)==mmap.end())///注意这里的find的用法,而且,,如果直接用mmap[str],即使没出现过也不会报错的!        {            cout<<"OK"<<endl;            mmap[str]=1;        }        else        {            int t=mmap[str];            cout<<str<<t<<endl;///注意这里输出的小bug,有i个要输出i,第一个的隐含下标是0            mmap[str]=t+1;        }    }    return 0;}


D - Mysterious Present

(最长上升子序列+路径记录或者路径回查,与上述B题的DP加路径回溯有异曲同工之妙,这里用的是for,上面用的是递归来解决)

给n个信封,用嵌套的形式把一个贺卡装起来,并且嵌套的信封还有贺卡都得是一个size大于另一个,size有两个参量weight,height

数据规模, nwh (1  ≤ n ≤ 50001 ≤ w,  h  ≤ 106)    ,信封的尺寸也是:(1 ≤ wi,  hi ≤ 106).   但是,问做多能用多少个信封?

思路:最长上升子序列的方法,只不过一个限制条件变成两个限制条件,不光w还有h;  最长上升子序列是子序列,生成的序列的相对顺序与原数列相同,这里怎么办呢?  这里先把序列排序,现根据一个量排序,那么导致dp[i]一定从dp[j],j<i转移过来!至于,输出路径怎么办?  按照从最大的dp,向前找,最大的dp是m的话,前面一定有一个m-1,而且m-1一定能满足转移到m,否则就还有一个m-1符合条件,然后找到m-1之后就再找m-2,直到……      注意:这里不能从前往后找,因为有好多个起点1的话,我们无法确定那个才是真正的起点!     从后往前找一定能找到一条合适的路径,即使这条路径不唯一!找到路径之后把下标压缩到栈里面或者上端队列里面!  然后输出来,注意,输出原来的序号;  或者是按照序列的顺序输出,输出其名字(将原来的序号视为name,好理解)!

附我自己的代码:

#include <cstdio>#include <iostream>#include <algorithm>#include <cmath>#include <cstring>#include <queue>#include<stack>using namespace std;#define maxn 5010#define inf 0xfffffffstruct Node{    int x,y;    int no;///记录编号}nod[maxn],sno;bool cmp(Node a,Node b){    if(a.x==b.x)        return a.y<b.y;    return a.x<b.x;}int n,dp[maxn];bool isok(Node n1,Node n2){    return (n1.x>n2.x&&n1.y>n2.y);}int main(){    scanf("%d%d%d",&n,&sno.x,&sno.y);    for(int i=1;i<=n;++i)    {        scanf("%d%d",&nod[i].x,&nod[i].y);        nod[i].no=i;    }    sort(nod+1,nod+n+1,cmp);    int ind=0;    memset(dp,0,sizeof(dp));    int ind2=0,ans=0;    for(int i=1;i<=n;++i)    {        if(!ind&&isok(nod[i],sno))ind=i;///逗号表达式        if(isok(nod[i],sno))dp[i]=1,ind2=i,ans=1;    }    for(int i=ind;i<=n;++i)    {        for(int j=ind;j<i;++j)        {            if(isok(nod[i],nod[j])&&isok(nod[i],sno)&&isok(nod[j],sno))               if(dp[j]+1>dp[i]) dp[i]=dp[j]+1,ans=max(dp[i],ans);        }if(dp[i]>dp[ind2])ind2=i;///这句话放到这里合适,因为放的位置不合适,导致求出来的不是最大的值的下标,        ///有时候,代码越简单,要求越高,一不小心就会出错!    }    stack<int>sta;    if(ind2)sta.push(ind2);    for(int i=ind2-1;i>=1;--i)    {        if(dp[ind2]-dp[i]==1&&dp[i]!=0&&isok(nod[ind2],nod[i]))        {            ind2=i;            sta.push(ind2);        }    }    cout<<sta.size()<<endl;    while(sta.size()>1)    {        printf("%d ",nod[sta.top()].no);sta.pop();    }if(sta.size()==1)printf("%d\n",nod[sta.top()].no),sta.pop();  return 0;}


附被人的双端队列的代码:下面这个代码在某些地方我觉得不如我的代码简介,但是我的代码因为简介在调试的过程中出现了好多小bug!  

#include <cstdio>#include <iostream>#include <algorithm>#include <cmath>#include <cstring>#include <queue>using namespace std;#define maxn 5010#define inf 0xfffffffstruct node{    int x,y;    int no;///记录编号}p[maxn];bool cmp(node a,node b){    if(a.x==b.x)        return a.y<b.y;    return a.x<b.x;}int dp[maxn];int main(){    int sx,sy;    int n;    scanf("%d",&n);    scanf("%d%d",&sx,&sy);    //if(sx>sy)        //swap(sx,sy);    for(int i=1;i<=n;i++)    {        scanf("%d%d",&p[i].x,&p[i].y);        p[i].no=i;        //if(p[i].x>p[i].y)            //swap(p[i].x,p[i].y);    }    sort(p+1,p+n+1,cmp);    int ctag=n+1;    for(int i=0;i<=n;i++)        if(p[i].x>sx&&p[i].y>sy)        {            ctag=i;            break;        }    ///处理边界也就是所说的临界条件    for(int i=ctag;i<=n;i++)        if(p[i].x>sx&&p[i].y>sy)            dp[i]=1;        else            dp[i]=-inf;    ///DP 核心代码  类似于最长上升序列    for(int i=ctag;i<=n;i++)        for(int j=i+1;j<=n;j++)            if(p[j].x>p[i].x&&p[j].y>p[i].y&&p[j].x>sx&&p[j].y>sy)                dp[j]=max(dp[j],dp[i]+1);    int ans=0;    for(int i=1;i<=n;i++)    {        //cout<<dp[i]<<endl;        ans=max(ans,dp[i]);    }    deque<int>q;///传说中的双向队列    while(!q.empty())        q.pop_front();    int index=1;    for(int i=1;i<=n;i++)        if(dp[i]>dp[index]&&p[i].x>sx&&p[i].y>sy)            index=i;///寻找结尾而非开头    //cout<<index<<endl;    q.push_front(p[index].no);    for(int i=index-1;i>=1;i--)    {        if(q.size()==ans)            break;        if(p[index].x>p[i].x&&p[index].y>p[i].y&&dp[index]==dp[i]+1)        {            index=i;            q.push_front(p[i].no);            if(q.size()==ans)                break;        }    }    ///输出结果    cout<<ans<<endl;    if(ans)    {        while(q.size()>1)            cout<<q.front()<<" ",q.pop_front();///传说中的逗号表达式        cout<<q.front()<<endl;    }    return 0;}


E - Triangle

这个题没啥好说的,就是一个水题,但是题意并非那么好懂!
现在附上原题干仔细读读!
给四个木棍,看能否组成符合要求的三角形,非退化的,退化的,还有impossible;  退化的就是长边=两短边之和
E - Triangle
Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u
Submit Status Practice CodeForces 6A

Description

Johnny has a younger sister Anne, who is very clever and smart. As she came home from the kindergarten, she told his brother about the task that her kindergartener asked her to solve. The task was just to construct a triangle out of four sticks of different colours. Naturally, one of the sticks is extra. It is not allowed to break the sticks or use their partial length. Anne has perfectly solved this task, now she is asking Johnny to do the same.

The boy answered that he would cope with it without any difficulty. However, after a while he found out that different tricky things can occur. It can happen that it is impossible to construct a triangle of a positive area, but it is possible to construct a degenerate triangle. It can be so, that it is impossible to construct a degenerate triangle even. As Johnny is very lazy, he does not want to consider such a big amount of cases, he asks you to help him.

Input

The first line of the input contains four space-separated positive integer numbers not exceeding 100 — lengthes of the sticks.

Output

Output TRIANGLE if it is possible to construct a non-degenerate triangle. Output SEGMENT if the first case cannot take place and it is possible to construct a degenerate triangle. Output IMPOSSIBLE if it is impossible to construct any triangle. Remember that you are to use three sticks. It is not allowed to break the sticks or use their partial length.

Sample Input

Input
4 2 1 3
Output
TRIANGLE
Input
7 2 2 4
Output
SEGMENT
Input
3 5 9 1
Output
IMPOSSIBLE





F - President's Office

这个题还好,就是给一个矩阵,然后告诉你什么字母表示老板,然后老板的桌子四边相邻的其他字母是他的助手,问收多少助手?

找出老板的桌子坐标,然后遍历四周相邻的数数有多少不同颜色就可!

本来很简单的一个题,郁闷了我好久,各种读如错误,当年做字符串小题目的时候都喜欢用java,c++的各种字符串读入细节都不会;问题是我cin和scanf混着用的,cin不用在行末读换行!  我各种调试,各种报错!   最后好不容易经过我疯狂的调试对了的时候又在一个计数的地方出现bug了,在计数的时候我先让临时变量tmp=‘.’ ,表示空格,然后只要不能与tmp,ans++,然后tmp替换成‘.',自以为如果是点的话应该和最初的tmp相等就不用计数了,但是我忘了当tmp替换成新字符时,点有可能在这个时候出现!

其实在这种情况下,也就是说有多种情况要判断的时候多写一句话没什么坏处!  

当时脑子里还感觉这里可能有错误,但是没往深处想啊!   以为自己读错题了,各种想法纷纷冒出来,此时自己其实已经乱了阵脚,根本也去想如果太难的话不可能有那么多人过的!教训啊,比赛经验少的问题一下子就暴露出来了!

话说,下面的代码有两种解法,是在后来我对题目的一种误读!


#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;char map[105][105];bool DFS(int day,int hour,int tot){    return 1;}int n,m;char cmd;char cnt[500];int main(){    cin>>n>>m;    cin>>cmd;for(int i=0;i<500;++i)    cnt[i]='.';    string ss;   // cin>>ss;    for(int i=0; i<=n+1; ++i)    {        for(int j=0; j<=m+1; ++j)        {            map[i][j]='.';//cout<<map[i][j];        }//cout<<endl;    }    for(int i=1; i<=n; ++i)    {        for(int j=1; j<=m; ++j)        {           cin>>map[i][j];        }//cout<<"ok"<<endl;    }//  cout<<"==============================================="<<endl;//  for(int i=1; i<=n; ++i)//    {//        for(int j=1; j<=m; ++j)//        {//            cout<<map[i][j];//        }cout<<endl;;//    }cout<<"==============================================="<<endl;    int ans=0;    int ind1=0,ind2=0,ind3=0,ind4=0;    for(int i=1; i<=n; ++i)    {        for(int j=1; j<=m; ++j) if(map[i][j]==cmd)         {                if(ind1)ind2=i;                else ind1=i;                if(ind3)ind4=j;                else ind3=j;        }    }if(ind2==0)ind2=ind1;if(ind4==0)ind4=ind3;//cout<<ind1<<ind2<<ind3<<ind4<<endl;int tot=0;    char tmp='.';    for(int j=ind3;j<=ind4;++j)    {        if(map[ind1-1][j]!='.')        {            cnt[++tot]=map[ind1-1][j];        }        if(map[ind1-1][j]!=tmp&&map[ind1-1][j]!='.')        {            ans++;   tmp=map[ind1-1][j];//cout<<"ok "<<ind1-1<<"===="<<j<<endl;        }    }      tmp='.';    //  if('.'==tmp)cout<<"hahahahahahahah"<<endl;    for(int j=ind3;j<=ind4;++j)    {        if(map[ind2+1][j]!='.')        {            cnt[++tot]=map[ind2+1][j];        }        if(map[ind2+1][j]!=tmp&&map[ind2+1][j]!='.')        {            ans++;   tmp=map[ind2+1][j];//cout<<"ok "<<ind2+1<<"====="<<j<<endl;        }    }      tmp='.';    for(int i=ind1;i<=ind2;++i)    {        if(map[i][ind3-1]!='.')        {            cnt[++tot]=map[i][ind3-1];        }        if(map[i][ind3-1]!=tmp&&map[i][ind3-1]!='.')        {             ans++;   tmp=map[i][ind3-1];///cout<<"ok "<<i<<endl;        }    }      tmp='.';    for(int i=ind1;i<=ind2;++i)    {         if(map[i][ind4+1]!='.')        {            cnt[++tot]=map[i][ind4+1];        }        if(map[i][ind4+1]!=tmp&&map[i][ind4+1]!='.')        {             ans++;   tmp=map[i][ind4+1];///cout<<"ok "<<i<<endl;        }    }//    sort(cnt+1,cnt+tot+1);//    ans=0;//    for(int i=1;i<=tot;++i)//    {//        if(cnt[i]!=cnt[i-1]&&cnt[i]!='.')ans++;//    }    printf("%d\n",ans);//cout<<"NO"<<Endl;    return 0;}


G - Alice, Bob and Chocolate

给n块巧克力,两个人从两端开始吃,吃每一块都要消耗一定时间,当两个人碰头都要吃同一块的时候,女生优先,问最后每个人吃的块数!

思路:枚举第i块,看第i块能否是Alice吃,是的话继续判断i+1块,知道某一块Alice不能吃,那么Alice吃i-1块,剩下的就是Bob吃了。第i块判断谁吃的条件就是两个人各位吃完各自的前一块,ALice就是i-1块,Bob就是i+1块用时,Time用时<=Bod的话Alice吃!

这个题思路比较清晰,可惜还没读到这个题比赛就结束了! 


#include<iostream>#include<cstdio>#include<cstring>const int M=100010;using namespace std;int n;int num[M],sum1[M],sum2[M];///num表示记录吃没块巧克力所用的时间,sum1[i]表示Alice从头吃,吃完i的时候用时;   sum2就是从后往前吃吃完i……int main(){    cin>>n;    memset(num,0,sizeof(num));    memset(sum1,0,sizeof(sum1));    memset(sum2,0,sizeof(sum2));    for(int i=1;i<=n;++i)    {        scanf("%d",&num[i]);        sum1[i]+=(sum1[i-1]+num[i]);    }    for(int i=n;i>=1;--i)        sum2[i]+=(num[i]+sum2[i+1]);    int ans=0;    for(int i=1;i<=n;++i)    {///枚举第i块要谁吃,当Bob吃的时候,alice就不能再吃了;   Alice要吃的条件就是吃完前一块所用时间大于等于Bob吃完前一块        if(!(sum1[i-1]<=sum2[i+1])){ans=i-1;break;}    }    if(n==1)ans=1;///特殊数据特殊处理cout<<ans<<" "<<n-ans<<endl;    return 0;}



H - Lizards and Basements 2

主人公是个魔法师在游戏里面,他要打死一排弓箭手,每次射向第i个人的一个火球对i造成a点伤害和两边的人造成b点的溅射伤害,问最少多少个火球能将对手全部杀死,并且输出攻击方案,也就是先射谁后射谁!

数据规模不大 n, a, b (3 ≤ n ≤ 101 ≤ b < a ≤ 10)     每个对手的血量是1-15!

仔细研究半天,这个题和我在URAL 上做的题类似,能用DFS解决,但最好的办法是DP,不超时; DFS的话面临超时的危险,一定要合理的剪枝优化或者在某处细节上用点贪心等优化下,如果有数学定理来说就更好了,但是这个题不光求最优解,还要方案,应该没有什么定理能解决的!

那么两个方向就确定好了

DFS+合理的优化(不优化超时的可能性太大了,事实证明不优化绝对不行)

DP……

啥经过这几天的DP训练,很多题都敢往DP上想了,虽然这个题还是没有构造出合理的状态……  DFS这几天写的比较好,URAL上的那个也是用DFS 解决的,想试下,但是没成功!    后来从网上找了份DFS的,具体DFS的方式不一样,人家的效率很高,但现在还没吃透……




I - Exposition

给一个数列,从中挑选出

  

原创粉丝点击