9.17 test solution.

来源:互联网 发布:vue.js和react.js 编辑:程序博客网 时间:2024/05/29 15:52

1.巧克力棒(chocolate.cpp/c/pas)

时空限制

时间限制:1s

空间限制:64MB

题目描述

LYK 找到了一根巧克力棒,但是这根巧克力棒太长了,LYK 无法一口吞进去。

具体地,这根巧克力棒长为 n,它想将这根巧克力棒折成 n 段长为 1 的巧克力棒,然后慢慢享用。

它打算每次将一根长为 k 的巧克力棒折成两段长为 ab 的巧克力棒,此时若a=b,则LYK 觉得它完成了一件非常困难的事,并会得到 1 点成就感。

LYK 想知道一根长度为n的巧克力棒能使它得到最多几点成就感。

输入输出格式

输入格式:

第一行一个数n

输出格式:

一个数表示答案。

输入输出样例

输入样例:

7

输出样例:

4

说明

对于20%的数据,n5

对于50%的数据,n20

对于80%的数据,n2000

对于100%的数据,n1000000000

样例解释

7 掰成3+4, 将 3 掰成 1+2, 将 4 掰成 2+2 获得 1 点成就感, 将剩下的所有 2 掰成 1+1 获得 3 点成就感。总共 4 点成就感。


solution

  • 胡乱试了几个手造的数据,自己玩了一下,发现 2n 是最优的。

  • 于是就把 n 拆成 2 的幂相加的形式,最后统计答案。

  • 还有就是 2n 能获得多少的成就感

  • f[i]2i 能获得的成就感,那就有

  • f[0]=0(显然这是对的)

  • f[i]=2f[i1]+1

  • 对于上面那个式子的理解是,把 2i 拆成两个2i1 获得 1 点成就感,两个2i1 可以获得2f[i1] 点成就感。

  • 所以我的想法是打一个f[i]2i 的表,然后枚举累加就可以了。

  • 打出来发现 f[i] 其实就是 2i1

code

#include<cstdio>#include<algorithm>#include<algorithm>using namespace std;const int f[30]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535,131071,262143,524287,1048575,2097151,4194303,8388607,16777215,33554431,67108863,134217727,268435455,536870911};const int two[30]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912};int main() {    int n;    scanf("%d",&n);    int ans=0;    for(int i=30;i>=0;i--) {        if(n<two[i]) continue;        n-=two[i];        ans+=f[i];        if(n==0) break;    }    printf("%d",ans);    return 0;}

2.LYK快跑!(run.cpp/c/pas)

时空限制

时间限制:5s

空间限制:64MB

题目描述

LYK 陷进了一个迷宫!
这个迷宫是网格图形状的。 LYK 一开始在(1,1)位置, 出口在 (n,m)

而且这个迷宫里有很多怪兽,若第 a 行第 b 列有一个怪兽,且此时 LYK 处于第 cd 列,此时这个怪兽对它的威胁程度为 |ac|+|bd|

LYK 想找到一条路径,使得它能从 (1,1) 到达 (n,m),且在途中对它威胁程度最小的怪兽的威胁程度尽可能大。

当然若起点或者终点处有怪兽时,无论路径长什么样,威胁程度最小的怪兽始终=0

输入输出格式

输入格式:

第一行两个数 n,m

接下来 n 行,每行 m 个数,如果该数为 0,则表示该位置没有怪兽,否则存在怪兽。

数据保证至少存在一个怪兽。

输出格式:

一个数表示答案。

输入输出样例

输入样例:

3 40 1 1 00 0 0 01 1 1 0

输出样例:

1

说明

对于20%的数据 n=1

对于40%的数据 n2

对于60%的数据 n,m10

对于80%的数据 n,m100

对于90%的数据 n,m1000

对于另外10%的数据 n,m1000 且怪兽数量100


solution

  • 考试的时候只拿了 40 分。GG

  • 说一下 40 分怎么拿

    • n=1 的时候,直接printf("0");,因为只有一条路径,而且因为必定有怪兽存在,所以你必须经过他,那威胁程度就是 0

    • n=2 的时候,需要判断一下是不是必须经过某个怪兽,必须经过就输出0,否则输出1

    • 下面这些情况是必须经过的:
      1.有两个怪兽不在同一行但是在同一列
      2.有两个怪兽不在同一行,且他们列的差值为1

    • 所以记录每个怪兽的位置,看他和其他的怪兽是否满足上面的情况。

    • 还有就是如果起点或终点有怪兽,也是直接printf("0");,因为每条路径必须经过这两个点,每条路径的min 都是0

  • 下面是100 分思路。

    • 一句话题解:二分答案+bfs

    • 求威胁程度最小的怪兽的威胁程度尽可能大,那应该就是二分答案啦。

    • 那先确定 lrl 显然是0,至于r|ac|+|bd|的最大值就是n1+m1=n+m2,所以r=n+m2

    • 然后就是二分答案中的 Judge (当然也可以叫 check )函数的问题。

    • Emmm,迷宫问题很容易想到搜索,而且搜索很容易想到 bfs,当然如果你想到了dfs,那你就会在TLE或者爆栈或者WA之后想到bfs,嗯,所以就用bfs。

    • 因为这个什么LYK在移动,所以每次移动之后暴力算威胁程度显然不太好

    • 所以可以预处理一下,用 dis[i][j] 表示走到(i,j)这个点的时候,怪兽对LYK的最小的威胁程度

    • 预处理也可以用bfs,把每个怪兽入队,然后bfs一下,更新距离。

    • 有了 dis 数组,Judge 函数就好写一点了,枚举每个方向,只有当 dis[i][j]mid 的时候才 push 进队列,为什么是呢,因为你要威胁程度最大,如果小了,那肯定不行啊。

    • 如果按照以上规则入队,能到(n,m),就return true ,否则return false

    • 再就是要注意去重,要不然就会走过来有走回去。好像只有我没注意到?

code

#include<queue>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define MAXN 1010#define MAXM 1010template<typename T>inline void input(T &x) {    x=0; T a=1;    register char c=getchar();    for(;c<'0'||c>'9';c=getchar())        if(c=='-') a=-1;    for(;c>='0'&&c<='9';c=getchar())        x=x*10+c-'0';    x*=a;    return;}const int dx[]={0,0,1,-1};const int dy[]={1,-1,0,0};struct Point {    int x,y;    Point(int x=0,int y=0):        x(x),y(y) {}};const Point S=Point(1,1);int dis[MAXN][MAXM];queue<Point>q;bool inq[MAXN][MAXM];//inq表示一个点是否push进queue过.int n,m;#define inf 0x7fffffffint G[MAXN][MAXM];#define inmap(v) (v.x>=1&&v.x<=n&&v.y>=1&&v.y<=m)#define inq(a) inq[a.x][a.y]#define dis(a) dis[a.x][a.y]void bfs_Getdis() {    while(!q.empty()) {        Point u=q.front();        q.pop();        for(int i=0;i<4;++i) {            Point v=Point(u.x+dx[i],u.y+dy[i]);            if(inmap(v)&&!inq(v)) {                dis(v)=min(dis(v),dis(u)+1);                q.push(v);                inq(v)=true;            }        }    }    return;}bool bfs_Judge(int mid) {    if(dis(S)<mid) return false;    //一个微小的剪枝,起点必须经过,起点的dis都要比mid小,肯定不行    while(!q.empty()) q.pop();    //这个是必须的,因为return true的时候有可能队列非空    memset(inq,0,sizeof(inq));    q.push(S);    inq(S)=true;    while(!q.empty()) {        Point u=q.front();        q.pop();        if(u.x==n&&u.y==m) return true;        for(int i=0;i<4;++i) {            Point v=Point(u.x+dx[i],u.y+dy[i]);            if(inmap(v)&&!inq(v)&&dis(v)>=mid) {                q.push(v);                inq(v)=true;            }        }    }    return false;}int main() {    input(n),input(m);    for(int i=1;i<=n;++i)        for(int j=1;j<=m;++j) {            input(G[i][j]);            dis[i][j]=inf;            if(G[i][j]) {                dis[i][j]=0;                q.push(Point(i,j));                inq[i][j]=true;            }        }    if(G[1][1]==1||G[n][m]==1||n==1) {        printf("0");        return 0;    }    bfs_Getdis();    int l=0,r=n+m-2,ans;    while(l<=r) {        int mid=l+r>>1;        if(bfs_Judge(mid)) {            l=mid+1;            ans=mid;        } else r=mid-1;    }    printf("%d",ans);    return 0;}

3.仙人掌(cactus.cpp/c/pas)

时空限制

时间限制:1s

空间限制:64MB

题目描述

LYK 在冲刺清华集训(THUSC)于是它开始研究仙人掌,它想来和你一起分享它最近研究的结果。

如果在一个无向连通图中任意一条边至多属于一个简单环 (简单环的定义为每个点至多经过一次) ,且不存在自环,我们称这个图为仙人掌。

LYK 觉得仙人掌还是太简单了,于是它定义了属于自己的仙人掌。

定义一张图为美妙的仙人掌, 当且仅当这张图是一个仙人掌且对于任意两个不同的点 i,j,存在一条从 i 出发到 j 的路径,且经过的点的个数为 |ji|+1 个。 给定一张 n 个点 m 条边且没有自环的图,LYK 想知道美妙的仙人掌最多有多少条边。

数据保证整张图至少存在一个美妙的仙人掌。

输入输出格式

输入格式:

第一行两个数n,m,表示这张图的点数和边数。

接下来 m 行,每行两个数 u,v 表示存在一条连接 u,v 的无向边。

输出格式:

一个数表示答案

输入输出样例

输入样例:

4 61 21 31 42 32 43 4

输出样例:

4

说明

对于20%的数据 n3

对于40%的数据 n5

对于60%的数据 n8

对于80%的数据 n1000

对于100%的数据 n100000mmin(200000,n(n1)/2)

样例解释

选择边12,13,23,34,能组成美妙的仙人掌,且不存在其它美妙仙人掌有超过 4 条边。


solution

考试的时候一脸懵逼,woc这什么东西。

于是开始想各种奇怪的骗分,n3 的时候好像可以手玩。

n5 好像也可以手玩。

就这样活生生把前 40 分搞成了提交答案题。

最后脑洞大开,强行给每条边加一个负边权,跑一遍spfa,然后找到第一个入队次数超过 n 的节点,统计和这个点有关系的边数。

结果全天下都是 40,lz 10分。fuck

zyh输出 n 得了30 分,GG。

  • 下面才是题解

  • 观察美丽的仙人掌的定义,发现编号为 ii+1 的点之间必存在一条边

  • 问题转化成有若干区间,求最多的区间,使得区间之间没有重叠和覆盖。

  • 然后贪心或者dp都可以。

  • 看不懂吗?Emmm 我也看不懂

code

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define MAXN 100010template<typename T>inline void input(T &x) {    x=0; T a=1;    register char c=getchar();    for(;c<'0'||c>'9';c=getchar())        if(c=='-') a=-1;    for(;c>='0'&&c<='9';c=getchar())        x=x*10+c-'0';    x*=a;    return;}int f[MAXN],g[MAXN];//g[i]表示和i相连的边中,最大的起点编号。int main() {    int n,m;    input(n),input(m);    while(m--) {        int u,v;        input(u),input(v);        if(u>v) swap(u,v);        if(u+1!=v) g[v]=max(g[v],u);    }    f[0]=-1;    for(int i=2;i<=n;i++)        f[i]=max(f[i-1],f[g[i]]+1);    printf("%d",f[n]+n-1);    //n-1是链上的那些边    return 0;}