10.1 test solution.

本文解析了三道算法竞赛题目:字符串权值计算、利用镜子实现视线传递及八数码问题的变种。介绍了每道题目的核心算法思路,如预处理、计算几何判断和图搜索等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.a(a.cpp/c/pas)

时间限制

1000ms

空间限制

256MB

【问题描述】

你是能看到第一题的 friends呢。 —— hja

何大爷对字符串十分有研究,于是天出题虐杀 zhx。何大爷今天为字符串定义了新的权值计算方法。一个字符串由小写字母组成,字符串的权值被定义为其中出现次数最多的字符的次数减去少出现次数最少的字符的次数。(注 被定义为其中出现次数最多的字符减去少。(注意,在讨论出现最少的字符的时候该字符必须至少出现一次)现在何大爷给你一个字符串,何大爷想知道这个字符串的所有子串中权值最大的权值是多少?

【输入格式】

第一行 一个整数 n,代表字符串的长度 。
接下来一行 n 个小写字母,代表该字符串。

【输出格式】

一行个整数代表答案。

【样例输入】

10
aabbaaabab

【样例输出】

3

【数据范围与规定】

对于 30% 的数据, 1n100
对于 60% 的数据, 1n1000
对于 100% 的数据, 1n106


solution

  • 60分

    • 暴力枚举每个字串,对于每个字串更新一次答案。
  • 100分

    • sum[c][i] 表示 c 这个字符到 i 这个位置出现的次数

    • l 表示子串的左端点,r 表示右端点

    • 那对于一个子串他的权值就是max{sum[c][r]sum[c][l1]}min{sum[c][r]sum[c][l1]} ,其中 c 是小写字母

    • 假设在每个子串中出现次数最多的那个字符为 a ,最小的为 b

    • ans=sum[a][r]sum[a][l1](sum[b][r]sum[b][l1])

    • =sum[a][r]sum[b][r]+sum[b][l1]sum[a][l1]

    • =sum[a][r]sum[b][r](sum[a][l1]sum[b][l1])

    • 计算上面那个式子的max,需要枚举两个字符a,b和两个端点l,r

    • 时间复杂度是O(n2262),会TLE

    • 先把262优化到 26

    • 每次移动端点仅仅是新加入一个字符,也就是说只有这一个新加入的字符会影响答案,所以只用这个字符和其余的字符做差然后取绝对值就好了,并不用挨个枚举更新答案

    • 然后再把 n2 优化到 n

    • 因为对于最后的答案我们并不在意 l,r,所以看一下能不能省掉一维状态

    • 对于每次右端点的移动,可能它对应的最右的左端点和上次一样

    • 也就是说右端点的移动不一定会让 (sum[a][l1]sum[b][l1]) 的最小值更新

    • 所以用一个数组 Min[a][b] 来保存 (sum[a][l1]sum[b][l1]) 的最小值

    • 然后sum数组直接省掉第二维,表示的是到当前右端点字符出现的次数之和

    • 假设出现在右端点的字符是a,其余的字母是 b,那么每次移动右端点

    • ans=max{sum[a]sum[b]Min[a][b],sum[b]sum[a]Min[b][a]}

    • 这样来更新还是有BUG的,你还需要记录最后更新 Min[a][b] 的位置和 b 最后出现的位置

    • 如果这两个位置相同, sum[a]sum[b]Min[a][b]的值还要再1

    • 这个东西很好想的,自己想一下就可以想出来,我就不再赘述啦

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 1000010

int sum[30],last[30];
int Min[30][30],pos[30][30];

char s[MAXN];

int main() {
    int n;
    scanf("%d%s",&n,s+1);
    int ans=-1;
    for(int i=1;i<=n;i++) {
        int a=s[i]-'a'+1;
        sum[a]++;
        last[a]=i;
        for(int b=1;b<=26;b++) 
            if(b!=a&&sum[b]!=0) {
                ans=max(ans,sum[a]-sum[b]-Min[a][b]-(last[b]==pos[a][b]));
                ans=max(ans,sum[b]-sum[a]-Min[b][a]-(last[b]==pos[b][a]));
            }
        for(int b=1;b<=26;b++) {
            if(sum[a]-sum[b]<Min[a][b]) {
                Min[a][b]=sum[a]-sum[b];
                pos[a][b]=i;
            }
            if(sum[b]-sum[a]<Min[b][a]) {
                Min[b][a]=sum[b]-sum[a];
                pos[b][a]=i;
            }
        }
    }
    printf("%d",ans);
    return 0;
}

2.b(b.cpp/c/pas)

时间限制

1000ms

空间限制

256MB

【问题描述】

你是能看到第二题的 friends 呢。 ——laekov

Hja 和 Yjq 在玩捉迷藏。Yjq 躲了起来,Hja 要找他。在他们玩游戏的房间里,只有一堵不透明的墙和一个双面的镜子。Hja 和 Yjq 可以看作平面上坐标分别为(Xv,Yv)(Xp,Yp)的点。墙是一条连接(Xw1,Yw1)(Xw2,Yw2)的线段,镜子是一条连接(Xm1,Ym1)(Xm2,Ym2)的线段。

如果视线和障碍物有公共点,那么我们认为视线会被阻挡,无法看见。如果视线和镜子有公共点,那么我们认为发生了反射。反射的过程遵循物理规律——入射角等于反射角,且反射光线与入射光线在镜子同侧。也就是说,想要看见对方,Hja 和 Yjq 必须在镜子的同一侧,包括镜子所在直线上(参见样例 1)。如果视线与镜子重合,那么不会发生反射,并且镜子不被当作障碍物(参见样例 4)。

Hja 很想知道他站在原地能否看见 Yjq,帮助他解决这个问题。

【输入格式】

第一行两个数Xv,Yv,表示 Hja 的坐标。
第二行两个数Xp,Yp表示 Yjq 的坐标。
第三行四个数Xw1,Yw1,Xw2,Yw2,分别表示墙的两个端点的坐标。
第四行四个数Xm1,Ym1,Xm2,Ym2,分别表示镜子的两个端点的坐标。

【输出格式】

如果 Hja 站在原地能看到 Yjq,则输出”YES”,否则输出”NO”。

【样例输入 1】

-1 3
1 3
0 2 0 4
0 0 0 1

【样例输出 1】

NO

【样例输入 2】

0 0
1 1
0 1 1 0
-100 -100 -101 -101

【样例输出 2】

NO

【样例输入 3】

0 0
1 1
0 1 1 0
-1 1 1 3

【样例输出 3】

YES

【样例输入 4】

0 0
10 0
100 100 101 101
1 0 3 0

【样例输出 4】

YES

【数据规模与约定】

对于100%的数据,所有坐标均为绝对值不超过104的整数。输入的线段不会退化成点,且两条线段没有交点。Hja 和 Yjq 的位置不同,且不在任何一条线段上。


solution

  • hja只有在两种情况下可以看见yjq

  • 不需要镜子就可以直接看见yjq

qwq

  • 直接看视线被墙挡住,但可以通过镜子看见

qwq

  • 所以就可以先判断能否直接看见,在判断可不可以通过镜子看见

  • 不能通过镜子看见有三种情况

  • 入射光线被墙挡住

qwq

  • 反射光线被墙挡住

qwq

  • 两个人不在镜子的同一侧

qwq

  • 还有就是要注意题目中说的一些特殊情况

  • 这种情况也可以看见
    qwq

  • 算是一道计算几何入门题目

code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const double eps=1e-8;

int sgn(double x) {
    if(fabs(x)<eps) return 0;
    if(x>0.00) return 1;
    return -1;
}

struct Point {
    double x,y;
    Point(double x=0.00,double y=0.00):
        x(x),y(y) {}
    void SCANF() {
        scanf("%lf%lf",&x,&y);
        return;
    }
    Point operator + (const Point &q) const {
        return Point(x+q.x,y+q.y);;
    }
    Point operator - (const Point &q) const {
        return Point(x-q.x,y-q.y);
    }
    Point operator * (const double &q) const {
        return Point(x*q,y*q);
    }
};

Point hja,yjq,w1,w2,m1,m2;

double cross(Point a,Point b) {
    return a.x*b.y-b.x*a.y;
}

double dot(Point a,Point b) {
    return a.x*b.x+a.y*b.y;
}

double cross(Point p1,Point p2,Point p3,Point p4) {
    if(sgn(cross(p2-p1,p3-p1))*sgn(cross(p2-p1,p4-p1))==1)
        return false;
    if(sgn(cross(p4-p3,p1-p3))*sgn(cross(p4-p3,p2-p3))==1)
        return false;
    if(sgn(max(p1.x,p2.x)-min(p3.x,p4.x))==-1)
        return false;
    if(sgn(max(p1.y,p2.y)-min(p3.y,p4.y))==-1)
        return false;
    if(sgn(max(p3.x,p4.x)-min(p1.x,p2.x))==-1)
        return false;
    if(sgn(max(p3.y,p4.y)-min(p1.y,p2.y))==-1)
        return false;
    return true;
}

Point GetCross(Point p1,Point p2,Point p3,Point p4) {
    double a=p2.y-p1.y;
    double b=p1.x-p2.x;
    double c=-p1.x*p2.y+p1.y*p2.x;
    double d=p4.y-p3.y;
    double e=p3.x-p4.x;
    double f=-p3.x*p4.y+p3.y*p4.x;
    double x=(b*f-c*e)/(a*e-b*d);
    double y=(a*f-c*d)/(b*d-a*e);
    return Point(x,y);
}

Point calc_foot(Point p1,Point p2,Point p3) {
    double ratio=dot(p1-p2,p3-p2)/dot(p3-p2,p3-p2);
    return p2+(p3-p2)*ratio;
}

bool Judge() {
    if(!cross(hja,yjq,w1,w2)) {
        if(!cross(hja,yjq,m1,m2)) return true;
        if(!sgn(cross(m1-hja,m2-hja))&&!sgn(cross(m1-yjq,m2-yjq)))
            return true;
    }
    if(sgn(cross(m2-m1,hja-m1))*sgn(cross(m2-m1,yjq-m1))==1) {
        Point foot=calc_foot(yjq,m1,m2);
        foot=foot*2.0-yjq;
        if(cross(hja,foot,m1,m2)) {
            foot=GetCross(hja,foot,m1,m2);
            if(!cross(hja,foot,w1,w2)&&!cross(foot,yjq,w1,w2))
                return true;
        }
    }
    return false;
}

int main() {
    hja.SCANF(),yjq.SCANF();
    w1.SCANF(),w2.SCANF();
    m1.SCANF(),m2.SCANF();
    puts(Judge()?"YES":"NO");
    return 0;
}

3.c(c.cpp/c/pas)

时间限制

1000ms

空间限制

256MB

【问题描述】

你是能看到第三题的 friends 呢。——aoao

众所周知,八数码问题是一个非常难的问题,但是 Yjq 非常有面子,他把这道题简化了一番。现在给了你一个3 × 3的方格图,你的目标是通过不断移动使得相邻颜色的块形成联通块。你每次的移动方式是选择一列或者一行进行置换滑动(这个解释起来比较麻烦,看下面的图就懂了)。所谓置换滑动,就是所有格子沿着给定的方向顺次移动,最后一个格子会被置换到最前面的过程。现在给定整个方格图,以及每个格子是否能够移动,求使得相同颜色联通的最小步数。

【输入格式】

输入为3 × 3的方格图,每个位置由五个字符组成,前四个字符分别表示上下
左右四个部分的颜色,第五个字符表示该格子是否能够移动,其中 0 是能移动 1
不能移动。

【输出格式】

一行一个整数代表答案。

【样例输入】

GGGG0 GGGG0 GGGG0
OGOO0 GGGG0 OGOO0
OOOO0 OGGG1 OOOO0

【样例输出】

5

【样例解释】

qwq

【数据规模与约定】

对于100%的数据,所有颜色只可能是 RGBO 中的一种,且一定有解。


solution

  • 因为每个格子都包含4个三角形,所以直接把一个格子看成4个点然后建图

qwq

  • 然后就是爆搜,zhx:爆搜真的没什么好讲的.

code

200+行一点也不长呢

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct Edge {
    int u,v,next;
    Edge(int u=0,int v=0,int next=0):
        u(u),v(v),next(next) {}
};

Edge edge[110];
int head[50],cnt;

void addedge(int u,int v) {
    edge[++cnt]=Edge(u,v,head[u]);
    head[u]=cnt;
    edge[++cnt]=Edge(v,u,head[v]);
    head[v]=cnt;
    return;
}

struct rec {
    int s,step;
    rec(int s=0,int step=0):
        s(s),step(step) {}
};

int tmp[5][5],color[50];
int Map[10][5];
int nowmap[5][5],newmap[5][5];

bool num[10],use[87654325];
bool vis[50],col_find[5];
bool col[5],row[5];

char ch[10];

queue<int> q;

bool Judge(int now_s) {
    memset(num,false,sizeof(num));
    for(int i=3;i>=1;i--)
        for(int j=3;j>=1;j--)
            if(i!=3||j!=3) {
                tmp[i][j]=now_s%10;
                num[now_s%10]=true;
                now_s/=10;
            }
    for(int i=0;i<9;i++)
        if(!num[i]) {
            tmp[3][3]=i;
            break;
        }
    int Count=0;
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            for(int k=1;k<=4;k++)
                color[++Count]=Map[tmp[i][j]][k];
    memset(vis,false,sizeof(vis));
    memset(col_find,false,sizeof(col_find));
    for(int i=1;i<=36;i++)
        if(!vis[i]) {
            if(col_find[color[i]])
                return false;
            col_find[color[i]]=true;
            for(;!q.empty();q.pop());
            q.push(i);
            vis[i]=true;
            while(!q.empty()) {
                int u=q.front();
                q.pop();
                for(int j=head[u];j;j=edge[j].next) {
                    int v=edge[j].v;
                    if(color[v]==color[u]&&!vis[v]) {
                        vis[v]=true;
                        q.push(v);
                    }
                }
            }
        }
    return true;
}

queue<rec> Q;

int bfs(int now_s) {
    int step=0;
    rec now=rec(now_s,0);
    Q.push(now);
    use[now_s]=true;
    while(!Q.empty()) {
        now=Q.front();
        Q.pop();
        now_s=now.s;
        step=now.step;
        memset(num,false,sizeof(num));
        for(int i=3;i>=1;i--)
            for(int j=3;j>=1;j--)
                if(i!=3||j!=3) {
                    nowmap[i][j]=now_s%10;
                    num[now_s%10]=true;
                    now_s/=10;
                }
        for(int i=0;i<9;i++)
            if(!num[i]) {
                nowmap[3][3]=i;
                break;
            }
        int new_s,T;
        for(int i=1;i<=3;i++) {
            if(!row[i]) {
                for(int j=1;j<=3;j++)
                    for(int k=1;k<=3;k++)
                        newmap[j][k]=nowmap[j][k];
                T=newmap[i][1];
                newmap[i][1]=newmap[i][2];
                newmap[i][2]=newmap[i][3];
                newmap[i][3]=T;
                new_s=0;
                for(int j=1;j<=3;j++)
                    for(int k=1;k<=3;k++)
                        if(j!=3||k!=3) 
                            new_s=new_s*10+newmap[j][k];
                if(!use[new_s]) {
                    use[new_s]=true;
                    if(Judge(new_s)) return step+1;
                    Q.push(rec(new_s,step+1));
                }
                T=newmap[i][1];
                newmap[i][1]=newmap[i][2];
                newmap[i][2]=newmap[i][3];
                newmap[i][3]=T;
                new_s=0;
                for(int j=1;j<=3;j++)
                    for(int k=1;k<=3;k++)
                        if(j!=3||k!=3) 
                            new_s=new_s*10+newmap[j][k];
                if(!use[new_s]) {
                    use[new_s]=true;
                    if(Judge(new_s)) return step+1;
                    Q.push(rec(new_s,step+1));
                }
            }
            if(!col[i]) {
                for(int j=1;j<=3;j++)
                    for(int k=1;k<=3;k++)
                        newmap[j][k]=nowmap[j][k];
                T=newmap[1][i];
                newmap[1][i]=newmap[2][i];
                newmap[2][i]=newmap[3][i];
                newmap[3][i]=T;
                new_s=0;
                for(int j=1;j<=3;j++)
                    for(int k=1;k<=3;k++)
                        if(j!=3||k!=3) 
                            new_s=new_s*10+newmap[j][k];
                if(!use[new_s]) {
                    use[new_s]=true;
                    if(Judge(new_s)) return step+1;
                    Q.push(rec(new_s,step+1));
                }
                T=newmap[1][i];
                newmap[1][i]=newmap[2][i];
                newmap[2][i]=newmap[3][i];
                newmap[3][i]=T;
                new_s=0;
                for(int j=1;j<=3;j++)
                    for(int k=1;k<=3;k++)
                        if(j!=3||k!=3) 
                            new_s=new_s*10+newmap[j][k];
                if(!use[new_s]) {
                    use[new_s]=true;
                    if(Judge(new_s)) return step+1;
                    Q.push(rec(new_s,step+1));
                }
            }
        }
    }
    return 0;
}

#define id(i,j,k) ((i-1)*12+(j-1)*4+k)

int main() {
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++) {
            addedge(id(i,j,1),id(i,j,3));
            addedge(id(i,j,1),id(i,j,4));
            addedge(id(i,j,2),id(i,j,3));
            addedge(id(i,j,2),id(i,j,4));
            if(i!=3) addedge(id(i,j,2),id(i+1,j,1));
            if(j!=3) addedge(id(i,j,4),id(i,j+1,3));
        }
    int Count=0;
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++) {
            scanf("%s",ch+1);
            for(int k=1;k<=4;k++)
                if(ch[k]=='R') Map[Count][k]=0;
                else if(ch[k]=='G') Map[Count][k]=1;
                else if(ch[k]=='B') Map[Count][k]=2;
                else Map[Count][k]=3;
            if(ch[5]=='1') row[i]=col[j]=true;
            Count++;
        }
    int now_s=1234567;
    if(Judge(now_s)) {
        puts("0\n");
        return 0;
    }
    printf("%d\n",bfs(now_s));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值