2018.5.19 模拟赛 解题报告&总结

110 篇文章 1 订阅
107 篇文章 1 订阅

目录

前言

xmq x m q 不再,第一水过。。。
然而只有260。。。

总结

学了 LCA L C A 。。。
然后,就没有然后了。。。
放几篇博文吧。。。


商务旅行(倍增和tarjan做法皆有)


POJ 1330(LCA板子题,不过本人只用了tarjan做)


货车运输(算是一道比较难的LCA的题了,值得一刷)


题目

1
2
3

第一题 最大面积

大意

给定一个半径为 r r 的圆,放入两个长为a,宽为 b b 的长方形,问在面积最大的情况下可以得到最大的a b b

思路

利用勾股定理暴力模拟

代码

空间复杂度:O(1)
时间复杂度: O(2r) O ( 2 r )

#include<cstdio>
#include<cmath>
#define sqr(x) (x*x)
using namespace std;int n,a,b;double tmpi,tmpj,best,tmp;
int main()
{
    scanf("%d",&n);n*=2;
    for(int i=1;i<n;i++)
     for(int j=1;j<n;j++)
     {
        tmpi=sqrt(sqr(n/2.0)-sqr(i/2.0))*2;
        tmpj=sqrt(sqr(n/2.0)-sqr(j/2.0))*2;//勾股定理计算小长方形的长和宽
        tmp=tmpi*i+tmpj*j-i*j;//计算面积
        if(tmp>best)
        {
            best=tmp;
            a=i;
            b=j;//保存最优解
        }
     }
    printf("%d\n%d",a,b);//输出
}

第二题 文件名排序

大意

排序

思路

排序
(什么排序都行,因为它数据是真的弱)

代码(string)

该算法的优势就是比较时更为方便

#include<cstdio>
#include<iostream>
#include<algorithm>
#define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout)
#define check1 (a[i].d<a[j].d)
#define check2 (a[i].d==a[j].d&&a[i].d==1&&a[i].kzm>a[j].kzm)
#define check3 (a[i].d==a[j].d&&a[i].d==1&&a[i].kzm==a[j].kzm&&a[i].wjm>a[j].wjm)
#define check4 (a[i].d==a[j].d&&!a[i].d&&a[i].wjm>a[j].wjm)//四种情况,题目当中有
using namespace std;int n,k;string s;
struct node
{
    string wjm,kzm;
    bool d;
    int xh,rank;
}a[101];
bool cmp(node x,node y){return x.xh<y.xh;}
int main()
{
    file(sort);
    scanf("%d\n",&n);
    for(int i=1;i<=n;i++)
    {
        cin>>s;k=-1;
        k=s.find('.');
        if(k!=-1)
        {
            a[i].wjm=s.substr(0,k);//保存文件名
            a[i].d=true;//标记有点
            a[i].kzm=s.substr(k+1,s.size()-a[i].wjm.size()-1);//扩展名
        }
        else
        a[i].wjm=s;//没有扩展名
        a[i].xh=i;
    }
    for(int i=1;i<n;i++)
     for(int j=i+1;j<=n;j++)
      if(check1||check2||check3||check4)
       swap(a[i],a[j]);//第一遍冒泡
    for(int i=1;i<=n;i++) a[i].rank=i;//保存排名
    sort(a+1,a+1+n,cmp);//重新排序
    for(int i=1;i<=n;i++) printf("%d\n",a[i].rank);//输出
}

代码(Cstring)

该算法的优势是速度更快,调试更轻松
时间复杂度: O(nlogn+n2) O ( n l o g n + n 2 )
空间复杂度: O( O ( 很大 ) )

#include<cstdio>
#include<cstring>
#include<algorithm>
#define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout)
using namespace std;int n,k,x,y,len1,len2;bool d;
struct node
{
    char wjm[101],kzm[101],s[101];
    int xh,rank;
}a[101];
bool cmp(node x,node y){return x.xh<y.xh;}
int check(char a[],char b[])
{
    for(int i=0;i<min(strlen(a),strlen(b));i++) 
    if(a[i]<b[i]) return -1;//不需交换
    else if(a[i]>b[i]) return 1;//需要交换
    if(strlen(a)<strlen(b)) return -1;//不需要交换
    if(strlen(a)==strlen(b)) return 0;//完全相等
    return 1;//需要交换
}
int find(char c,char s[])//查找点的位置
{
    for(int i=0;i<strlen(s);i++) if(s[i]==c) return i;
    return 0;
}
int main()
{
    file(sort);
    scanf("%d\n",&n);
    for(int i=1;i<=n;i++)
    {
        gets(a[i].s);d=0;len1=0;len2=0;
        for(k=0;k<strlen(a[i].s);k++)
        {
            if(d) a[i].kzm[len2++]=a[i].s[k];
            if(a[i].s[k]=='.') d=1;
            if(!d) a[i].wjm[len1++]=a[i].s[k];
        }
        a[i].xh=i;
    }
    for(int i=1;i<n;i++)
     for(int j=i+1;j<=n;j++)
     {
        x=find('.',a[i].s);
        y=find('.',a[j].s);
        int hz=check(a[i].kzm,a[j].kzm),qz=check(a[i].wjm,a[j].wjm);
        if(!x&&y) {swap(a[i],a[j]);continue;}//有扩展名的优先
        if(x&&y)
        {
            if(hz==-1) continue;//不需交换直接返回
            if(hz==1) {swap(a[i],a[j]);continue;}//交换
            if(qz==1) swap(a[i],a[j]);continue;//交换
        }
        if(!x&&!y&&qz==1) swap(a[i],a[j]);//交换
     }
     for(int i=1;i<=n;i++) a[i].rank=i;
     sort(a,a+1+n,cmp);//记得还要排一次
     for(int i=1;i<=n;i++) printf("%d\n",a[i].rank);
}

第三题 取数

思路

暴搜80(好像某人A了)
记搜AC
就是把滑雪的记搜方法改一下,因为它有这种情况
1 1
3 2
5 0
7 0
9 0
如果是滑雪的方法它会把3堵住导致并非最优解,所以我们对记搜数组做一个新的参数——方向,比如上面那个我们既可以记录为左边和下边,这样子就可以避免封路带来的不优化性,就可以达到动态规划的目的啦!

代码(暴搜)

空间复杂度: O(n2) O ( n 2 )
时间复杂度: O(4n4) O ( 4 n 4 )

#include<cstdio>
#include<cstring>
#include<algorithm>
#define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout)
using namespace std;int n,m,a[101][101],ans=1,now,tot;
bool vis[101][101];//标记数组,如果用这个就会超时,不用就会WA
const short dx[4]={-1,0,1,0};
const short dy[4]={0,1,0,-1};//四个方向
int read()
{
    int d=1,f=0;char c;
    while((c=getchar())<48||c>57) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
    while((c=getchar())>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
    return d*f;
}
void write(int x)
{
    if(x>9) write(x/10);putchar(x%10+48);return;
}
int dfs(int x,int y,int c)//暴力搜索
{
    if(vis[x][y]==tot||x<1||y<1||x>n||y>m||c<=0) return 0;
    int t=1;vis[x][y]=tot;
    for(int i=0;i<4;i++)
     if(c==a[x+dx[i]][y+dy[i]]-a[x][y])
      {t=max(t,dfs(x+dx[i],y+dy[i],c)+1);}//四个方向
    return t;
}
int main()
{
    file(pick);
    n=read();m=read();
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++) a[i][j]=read();
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      for(int k=0;k<4;k++)
       if(a[i+dx[k]][j+dy[k]]-a[i][j]>0)
        {tot++;ans=max(ans,dfs(i,j,a[i+dx[k]][j+dy[k]]-a[i][j]));if(now==n*m) break;}//暴力搜索
    write(ans);
}

代码(记搜)

时间复杂度: O(4n2) O ( 4 n 2 )
空间复杂度: O(4n2) O ( 4 n 2 )

#include<cstdio>
#include<cstring>
#include<algorithm>
#define check(x,y) (x<1||y<1||x>n||y>m)
#define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout)
using namespace std;int n,m,a[101][101],ans=1,now,tot,f[101][101][4];//记忆化
const short dx[4]={-1,0,1,0};
const short dy[4]={0,1,0,-1};//四个方向
int read()
{
    int d=1,f=0;char c;
    while((c=getchar())<48||c>57) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
    while((c=getchar())>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
    return d*f;
}
void write(int x)
{
    if(x>9) write(x/10);putchar(x%10+48);return;
}
int len(int x,int y,int dep)//计算它们之间的落差
{
    int nx=x+dx[dep],ny=y+dy[dep];
    if(check(nx,ny)) return 99999999;
    return abs(a[nx][ny]-a[x][y]);
}
void dfs(int x,int y,int dep)
{
    if(check(x,y)) return;//暴出范围推出
    if(f[x][y][dep]) return;//返回
    f[x][y][dep]=1;//最少长度为1
    int nx=x+dx[dep],ny=y+dy[dep];
    if(a[nx][ny]<=a[x][y]) return;//落差为负数,返回
    for(int i=0;i<4;i++)
    {
        dfs(nx,ny,i);
        if(len(nx,ny,i)!=len(x,y,dep)) continue;//隔差不等返回
        f[x][y][dep]=max(f[x][y][dep],f[nx][ny][i]+1);//保存最优解
    }
    return;
}
int main()
{
    file(pick);
    n=read();m=read();
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++) a[i][j]=read();
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      for(int k=0;k<4;k++)
      {
        dfs(i,j,k);//继续搜索
        ans=max(ans,f[i][j][k]);//保存最优解
      }
    write(ans);
}

第四题 航空公司

大意

求一张有向图的最小生成树中的最长边

思路

库鲁思卡尔或者 prim p r i m 算法
(本题 prim p r i m 更快)

代码

时间复杂度: O(n2logn2+n2) O ( n 2 l o g n 2 + n 2 )
空间复杂度: O(n2) O ( n 2 )

#include<algorithm>
#include<cstdio>
#include<cmath>
#define N 1001
#define min(a,b) a<b?a:b
#define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout)
using namespace std;int n,x[N],y[N],r[N],tot,f[N],k=0;double ans;
struct node{int from,to;double w;}edge[N*N];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void judge(int x,int y){f[find(x)]=find(y);return;}
bool check(int x,int y){return find(x)==find(y);}
void add(int u,int v)
{
    edge[++tot].from=u;
    edge[tot].to=v;
    edge[tot].w=sqrt((x[u]-x[v])*(x[u]-x[v])+(y[u]-y[v])*(y[u]-y[v]))-r[u]-r[v];//勾股定理算距离
}
bool cmp(node x,node y)
{
    return x.w<y.w;
}
int read()
{
    int d=1,f=0;char c;
    while((c=getchar())<48||c>57) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
    while((c=getchar())>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
    return d*f;
}
int main()
{
    file(air);
    n=read();
    for(int i=1;i<=n;i++) x[i]=read(),y[i]=read(),r[i]=read(),f[i]=i;
    for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) add(i,j);
    sort(edge+1,edge+1+tot,cmp);
    for(int i=1;i<=tot&&k<n;i++)
    if(!check(edge[i].from,edge[i].to)) 
     {
        k++;
        judge(edge[i].from,edge[i].to);//并查集合并
        ans=max(ans,edge[i].w);//保存最长边
     }
    if(ans!=floor(ans)) ans=floor(ans)+1;
    printf("%0.lf",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值