SSL Week 1 总结&解题报告

总结

日期事件刷的题
Day 0(周日)考试
Day 1(周一)做题方程的解数,集合,魔板
Day 2(周二)改题
Day 3(周三)做题赌徒
Day 4(周四)做题
Day 5(周五)做题口袋的天空信息传递
Day 6(周六)模拟赛模拟赛的题

时间复杂度

题目正解时间复杂度
第一题 Closest O(nlog10(n)) O ( n l o g 10 ( n ) )
第二题 给出字符串 O(n3) O ( n 3 )
第三题 平台 O(n) O ( n )
第四题 腾讯大战360 O(2kE) O ( 2 k E )

解题报告

第一题 Closest

【题面】

考虑两个n位的十进制正整数A和B,都没有前导0。我们需要找到两个最近的靠近A的n位数(第一个比A大或与A相等,第二个严格比A小),使得它们的十进制表示是B中所有数字的某个排列。
对于给定的A和B,写一个程序closest找到这些“最靠近A”的数字,或者判断它们中的一个不存在。

【输入】

输入文件closest.in包含2行:
第1行为一个正整数A。
第1行为一个正整数B。
(A,B均为n位的正整数)

【输出】

输出文件closest.out共有2行。

第一行:最小的不比A小的n位数,没有前导0,包含B中的所有字符,以某一顺序排列。如果这样的数不存在,那么输出0。
第二行:最大的比A小的n位数,没有前导0,包含B中的所有字符,以某一顺序排列。如果这样的数不存在,那么输出0。

【输入输出样例1】
closest.inclosest.out
30754066
66040
【输入输出样例2】
closest.inclosest.out
30002034244556
45624542655444

竖着看,每次输入有两个数

【限制】
100%的数据满足:1<=n<=60

贪心思路

每个找到范围最接近的,再判断存不存在,要多加几个判断,这种方法可得80分。时间复杂度 O(2log10(n)) O ( 2 l o g 10 ( n ) )

代码
#include<cstdio>
#include<string>
#include<iostream>
#define r(i,a,b) for(int i=a;i<=b;i++)//正循环不解释
#define d(i,a,b) for(int i=a;i>=b;i--)//倒循环不解释
using namespace std;
char a[61],b[61];bool ok;//ok判断是否已经有所波动(即不完全相等)
int Bax[10],Bin[10],l1,l2,ans1[61],ans2[61];//l1为ans1的长度用来判断是否达成目标,ans1是第一个答案,ans2是第二个答案,Bax是每个数的数量,因为要用到两次,所以又开了个Bin
int main()
{
    freopen("closest.in","r",stdin);
    freopen("closest.out","w",stdout);
    cin>>a>>b;//输入
    r(i,0,strlen(b)-1) 
     {
        Bax[b[i]-48]++;
        Bin[b[i]-48]++;//统计
     }
    ok=true;//无波动
    r(i,0,strlen(a)-1)
     if(ok)//无波动
      {
        if(Bax[a[i]-48])//正好符合
         {
            ans1[++l1]=a[i]-48;
            Bax[a[i]-48]--;
            continue;//继续
         }
        ok=false;//如果不正好符合则有波动
        r(j,a[i]-47,9)//寻找最优解
         if(Bax[j])
          {
            ans1[++l1]=j;
            Bax[j]--;
            break;
          }
      }
     else//有波动的话就从0到9找最小
      r(j,0,9)
       if(Bax[j])
        {
         ans1[++l1]=j;
         Bax[j]--;
         break;
        }
     if(l1<strlen(a)) putchar(48);
     else 
      r(i,1,l1) putchar(ans1[i]+48);//判断,输出
    cout<<endl;
    ok=true;//以下同理
    r(i,0,strlen(a)-1)
     if(ok)
      {
        if(Bin[a[i]-48])
         {
            ans2[++l2]=a[i]-48;
            Bin[a[i]-48]--;
            continue;
         }
        ok=false;
        d(j,a[i]-48,!i?1:0)
         if(Bin[j])
          {
            ans2[++l2]=j;
            Bin[j]--;
            break;
          }
      }
     else
      d(j,9,0)
       if(Bin[j])
        {
         ans2[++l2]=j;
         Bin[j]--;
         break;
        }
     if(l2<strlen(a)) putchar(48);
     else r(i,1,l2) putchar(ans2[i]+48);
}

dfs思路

这里也需要结合贪心的思想,但不同的是dfs可以“回头”,而贪心不能,这也是剩下20分的关键所在。

代码
#include<cstdio>
#include<string>
#include<iostream>
#define r(i,a,b) for(int i=a;i<=b;i++)
#define d(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
char a[61],b[61];bool ok;
short Bax[10],Bin[10],len=0,ans[61],n;//变量不解释
void big()//如果有波动,直接按照顺序输出
{
    ok=false;
    r(i,1,len) putchar(ans[i]+48);
    r(i,0,9)
     r(j,1,Bax[i])
      printf("%d",i);
}
void small()//同理,不过是倒序
{
    ok=false;
    r(i,1,len) putchar(ans[i]+48);
    d(i,9,0)
     r(j,1,Bin[i])
      printf("%d",i);
}
void dfs1(int dep,int x)//dep为当前深度,x为上一次的数字
{
    if(dep>n) return;//越过了则退出
    r(i,x,9)
     if(Bax[i])//存在
        {
            ans[++len]=i;Bax[i]--;//保存
            if(i>x) {big();return;}//出现波动
            if(len==n)//满足条件
            {
                big();
                return;
            }
            dfs1(dep+1,a[dep]-48);//继续搜
            if(!ok)return;//这里要记得判断,防止出现多组解
            ans[len--]=0;Bax[i]++;//回溯
        }
}
void dfs2(int dep,int x)
{
    if(dep>n) return;
    d(i,dep==n?x-1:x,0)//前面的是因为这个不能完全相等,所以要特判,以下同理
     if(Bin[i])
        {
            ans[++len]=i;Bin[i]--;
            if(i<x) {small();return;}
            if(len==n)
            {
                small();
                return;
            }
            dfs2(dep+1,a[dep]-48);
            if(!ok)return;
            ans[len--]=0;Bin[i]++;
        }
}
int main()
{
    freopen("closest.in","r",stdin);
    freopen("closest.out","w",stdout);
    cin>>a>>b;n=strlen(a);
    r(i,0,strlen(b)-1) 
     {
        Bax[b[i]-48]++;
        Bin[b[i]-48]++;
     }
    ok=true;
    dfs1(1,a[0]-48);if(ok) putchar(48);
    cout<<endl;len=0;
    ok=true;
    dfs2(1,a[0]-48);if(ok) putchar(48);
}

第二题 给出字符串

【题目描述】

给出一个由小写字母组成的字符串。你的任务是找出其最长的出现至少两次的子串的长度。这些重复出现的子串可以重叠(参见样例2)。

【输入】
输入文件ygas.in第一行包含该字符串。数据保证该字符串非空,由小写字母组成,且其长度不超过100。
【输出】
输出文件ygas.out包含一个数代表至少出现两次的最长子串的长度。
【输入输出样例1】
ygas.inygas.out
abcd0
【输入输出样例2】
ygas.inygas.out
ababa3
【输入输出样例3】
ygas.inygas.out
zzz2

思路

string库+map库轻松AC

代码
#include<iostream>
#include<cstdio>
#include<map>
#include<string>
#define r(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
string x;
map<string,bool>h;//判断是否出现,类似于桶
int main()
{
    freopen("ygas.in","r",stdin);
    freopen("ygas.out","w",stdout);
    cin>>x;//输入
    for(int i=x.size()-1;i>0;i--)//枚举长度,倒过来是为了满足最优解
     {
        r(j,0,x.size()-i)//枚举位置
         {
            h[x.substr(j,i)]=1;//取首串
            r(k,j+1,x.size()-i+1)//取尾串
             if(h[x.substr(k,i)])//已经存在
              {
                cout<<i;//直接输出
                return 0;
              }
            h[x.substr(j,i)]=0;//放出串
         }
     }
    cout<<0;//如果没找到输出0
    return 0;
}

第三题 平台

【问题描述】

为了进行一种游戏,现决定搭造一些平板,而各个平板的地址已经选定。基于最普遍的认识,没有任何支持物的平板不可能漂浮在空中。说的更精确些,任意一平板的两端必需有支柱或者它在另一块平板上。
你会得到各个平板在坐标系中的坐标(如左下图)。每一块平板的坐标都是由它的高度(与地板间的垂直距离)和它的水平方位(开始和结束)决定的。每个支柱都距它支撑的平板的边缘半个单位(如右下图)。
算出支持所有平板的支柱的总长度。

【输入】

输入文件platforme.in第一行包括1个整数N,1 ≤ N ≤ 100,即平板的总数。
接下来的N行每行都是一块平板的坐标,是相应的Y,X1和 X2。即高度和水平的边缘坐标。所有的数都是不大于10000的正整数且满足X2 > X1+1(也可这样理解,每一块平板的长度至少为2)。
输入保证任意两块平板间没有重叠部分。

【输出】

输出文件platforme.out要撑起所有平板所需的支柱的总长度。

【输入输出样例】
platforme.in
3
1 5 10
3 1 5
5 3 7
platforme.out
14

思路

暴力模拟

代码
#include<cstdio>
#define r(i,a,b) for(int i=a;i<=b;i++)
#define t(a) (a<<3)+(a<<1)+c-48
using namespace std;
int min(int x,int y){return x<y?x:y;}
int h[101],x[101],y[101],n,m1,m2,ans;
int read()//输入流不解释
{
    char c;int f=0;
    while((c=getchar())<48||c>57);f=t(f);
    while((c=getchar())>=48&&c<=57) f=t(f);
    return f;
}
int main()
{
    n=read();
    r(i,1,n)
     {h[i]=read();x[i]=read();y[i]=read();}//输入
    r(i,1,n)
    {
        m1=m2=h[i];//初始高度
        r(j,1,n)
         if(i!=j&&h[i]>h[j])//之间还有比它更高的
          {
            if(y[j]>x[i]&&x[j]<=x[i]) m1=min(m1,h[i]-h[j]);//左边界满足
            if(x[j]<y[i]&&y[j]>=y[i]) m2=min(m2,h[i]-h[j]);//右边界满足
          }
        ans+=m1+m2;//左右边界的和
    }
    printf("%d",ans);//输出
}

第四题 腾讯大战360

【问题描述】

2010年11月3日,是一个难忘的日子。 腾讯发布消息:存360则,不留QQ。留QQ,则须卸360。 360则表示360与QQ可以共存。 这也就标志着腾讯与360的大战就此开始!
现在,腾讯与360由于身处异地,非常迫切地想在最短的时间内相遇,然后干一架。但是由于双方的技术员都在努力地编程序想干掉对方,所以他们希望你来帮他们找到一个最好的方案使得相遇的时间最短。
在此我们定义“相遇”为:两个人皆在同一个有编号的城市上就可以了,并且这两个人均可以站在原地等另外一个人。也就是说,在这里我们不考虑两人在路中间相遇。

【输入】

输入数据第一行:N和M(用空格隔开) 表示这是一个N*N的图并且有M条边,第二行到第M+1行 为这个图的详细信息。
每行共有被空格隔开的三个数:a b c。表示编号为a的城市到编号为b的城市
有一个双向边,并且要过这条双向边所需要花费的时间为c。
最后一行有两个数:S和T,S表示腾讯所处的城市,T表示360所处的
城市

【输出】

输出只有一行,D,表示二者“相遇”的最短时间。当然,如果无法相遇则输出“Peace!”

【输入输出样例】
battle.in
3 3
1 2 1
2 3 1
1 3 1
1 3
battle.out
1
【数据范围】

每组都是 n=5000 m=5000 n = 5000   m = 5000 并且保证运算过程中的所有值都不会超过 117901063 117901063

思路

两遍SPFA:
第一遍算腾讯所在点到所有点的距离。
第二遍算360所在点到所有点的距离。
分别存到 ans1,ans2 a n s 1 , a n s 2 数组中,最后扫描一遍。
因为它提到了一个人要等另一个人,所以腾讯和360相遇的时间等于

Max(ans1i,ans2i) M a x ( a n s 1 i , a n s 2 i )

取所有方案中的最小值,即为
Ans=min(Ans,max(ans1i,ans2i)) A n s = m i n ( A n s , m a x ( a n s 1 i , a n s 2 i ) )

代码(非STL)
#include<cstdio>
#define INF 2147483648>>1
#define t(a) (a<<3)+(a<<1)+c-48
#define r(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int n,m,tot,la[100001],u,v,w,ans=INF>>1,P,Q,ans1[50001],ans2[50001];//数组不解释
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
struct node
{
    int next,to,w;
}a[100001];
void add(int u,int v,int w)//邻接表不解释
{
    a[++tot].next=la[u];
    a[tot].to=v;
    a[tot].w=w;
    la[u]=tot;
}
void SPFA1()//SPFA不解释
{
    bool vis[50001]={0};vis[P]=true;
    int head=0,tail=1,team[100001]={0},dis[50001]={0};team[1]=P;
    r(i,1,n) dis[i]=INF>>1;
    dis[P]=0;
    do
    {
        head=head%n+1;
        u=team[head];
        vis[u]=true;
        for(int i=la[u];i;i=a[i].next)
        {
            v=a[i].to;w=a[i].w;
            if(dis[u]+w<dis[v])
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    tail=tail%n+1;
                    team[tail]=v;
                    vis[v]=true;
                }
            }
        }
        vis[u]=false;
    }while(head!=tail);
    r(i,1,n) ans1[i]=dis[i];
}
void SPFA2()
{
    bool vis[50001]={0};vis[Q]=true;
    int head=0,tail=1,team[100001]={0},dis[50001]={0};team[1]=Q;
    r(i,1,n) dis[i]=INF>>1;
    dis[Q]=0;
    do
    {
        head=head%n+1;
        u=team[head];
        vis[u]=true;
        for(int i=la[u];i;i=a[i].next)
        {
            v=a[i].to;w=a[i].w;
            if(dis[u]+w<dis[v])
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    tail=tail%n+1;
                    team[tail]=v;
                    vis[v]=true;
                }
            }
        }
        vis[u]=false;
    }while(head!=tail);
    r(i,1,n) ans2[i]=dis[i];
}
int read()//输入流不解释
{
    char c;int f=0;
    while((c=getchar())<48||c>57);f=t(f);
    while((c=getchar())>=48&&c<=57) f=t(f);
    return f;
}
int main()
{
    n=read();m=read();
    r(i,1,m)
     u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
    P=read();Q=read();//输入不解释
    SPFA1();SPFA2();
    r(i,1,n) 
     ans=min(ans,max(ans1[i],ans2[i]));
    if(ans!=INF>>1)printf("%d",ans);else puts("Peace!");//输出不解释
    return 0;
}
代码(STL)
#include<cstdio>
#include<queue>
#define INF 2147483648>>1
#define t(a) (a<<3)+(a<<1)+c-48
#define r(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int n,m,tot,la[100001],u,v,w,ans=INF>>1,P,Q,ans1[50001],ans2[50001];
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
struct node
{
    int next,to,w;
}a[100001];
void add(int u,int v,int w)
{
    a[++tot].next=la[u];
    a[tot].to=v;
    a[tot].w=w;
    la[u]=tot;
}
void SPFA1()
{
    bool vis[50001]={0};vis[P]=true;
    int dis[50001]={0};queue<int>q;
    q.push(P);
    r(i,1,n) dis[i]=INF>>1;
    dis[P]=0;
    do
    {
        u=q.front();q.pop();
        vis[u]=true;
        for(int i=la[u];i;i=a[i].next)
        {
            v=a[i].to;w=a[i].w;
            if(dis[u]+w<dis[v])
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=true;
                }
            }
        }
        vis[u]=false;
    }while(!q.empty());
    r(i,1,n) ans1[i]=dis[i];
}
void SPFA2()
{
    bool vis[50001]={0};vis[Q]=true;
    int dis[50001]={0};
    queue<int>q;q.push(Q);
    r(i,1,n) dis[i]=INF>>1;
    dis[Q]=0;
    do
    {
        u=q.front();q.pop();
        vis[u]=true;
        for(int i=la[u];i;i=a[i].next)
        {
            v=a[i].to;w=a[i].w;
            if(dis[u]+w<dis[v])
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=true;
                }
            }
        }
        vis[u]=false;
    }while(!q.empty());
    r(i,1,n) ans2[i]=dis[i];
}
int read()
{
    char c;int f=0;
    while((c=getchar())<48||c>57);f=t(f);
    while((c=getchar())>=48&&c<=57) f=t(f);
    return f;
}
int main()
{
    n=read();m=read();
    r(i,1,m)
    u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
    P=read();Q=read();
    SPFA1();SPFA2();
    r(i,1,n) 
     ans=min(ans,max(ans1[i],ans2[i]));
    if(ans!=INF>>1)printf("%d",ans);else puts("Peace!");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值