1.巧克力棒(chocolate.cpp/c/pas)
时空限制
时间限制:1s
空间限制:64MB
题目描述
LYK 找到了一根巧克力棒,但是这根巧克力棒太长了,LYK 无法一口吞进去。
具体地,这根巧克力棒长为
n
,它想将这根巧克力棒折成
它打算每次将一根长为
LYK 想知道一根长度为
输入输出格式
输入格式:
第一行一个数 n 。
输出格式:
一个数表示答案。
输入输出样例
输入样例:
7
输出样例:
4
说明
对于
对于 50% 的数据, n≤20 。
对于 80% 的数据, n≤2000 。
对于 100% 的数据, n≤1000000000 。
样例解释
将
7
掰成
solution
胡乱试了几个手造的数据,自己玩了一下,发现 2n 是最优的。
于是就把 n 拆成
2 的幂相加的形式,最后统计答案。还有就是 2n 能获得多少的成就感
设 f[i] 是 2i 能获得的成就感,那就有
f[0]=0 (显然这是对的)
f[i]=2∗f[i−1]+1
对于上面那个式子的理解是,把 2i 拆成两个 2i−1 获得 1 点成就感,两个
2i−1 可以获得 2∗f[i−1] 点成就感。所以我的想法是打一个 f[i] 和 2i 的表,然后枚举累加就可以了。
打出来发现 f[i] 其实就是 2i−1
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
行第
LYK 想找到一条路径,使得它能从 (1,1) 到达 (n,m) ,且在途中对它威胁程度最小的怪兽的威胁程度尽可能大。
当然若起点或者终点处有怪兽时,无论路径长什么样,威胁程度最小的怪兽始终 =0 。
输入输出格式
输入格式:
第一行两个数 n,m 。
接下来
n
行,每行
数据保证至少存在一个怪兽。
输出格式:
一个数表示答案。
输入输出样例
输入样例:
3 4
0 1 1 0
0 0 0 0
1 1 1 0
输出样例:
1
说明
对于
对于 40% 的数据 n≤2 。
对于 60% 的数据 n,m≤10 。
对于 80% 的数据 n,m≤100 。
对于 90% 的数据 n,m≤1000 。
对于另外 10% 的数据 n,m≤1000 且怪兽数量 ≤100 。
solution
考试的时候只拿了 40 分。GG
说一下 40 分怎么拿
n=1 的时候,直接
printf("0");
,因为只有一条路径,而且因为必定有怪兽存在,所以你必须经过他,那威胁程度就是 0 。n=2 的时候,需要判断一下是不是必须经过某个怪兽,必须经过就输出 0 ,否则输出1 。下面这些情况是必须经过的:
1.有两个怪兽不在同一行但是在同一列
2.有两个怪兽不在同一行,且他们列的差值为1所以记录每个怪兽的位置,看他和其他的怪兽是否满足上面的情况。
还有就是如果起点或终点有怪兽,也是直接
printf("0");
,因为每条路径必须经过这两个点,每条路径的 min 都是 0 。
下面是
100 分思路。一句话题解:二分答案+bfs
求威胁程度最小的怪兽的威胁程度尽可能大,那应该就是二分答案啦。
那先确定 l 和
r 。 l 显然是0 ,至于 r ,|a−c|+|b−d| 的最大值就是 n−1+m−1=n+m−2 ,所以 r=n+m−2 。然后就是二分答案中的 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 1010
template<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 0x7fffffff
int 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
出发到
数据保证整张图至少存在一个美妙的仙人掌。
输入输出格式
输入格式:
第一行两个数 n,m ,表示这张图的点数和边数。
接下来
m
行,每行两个数
输出格式:
一个数表示答案
输入输出样例
输入样例:
4 6
1 2
1 3
1 4
2 3
2 4
3 4
输出样例:
4
说明
对于 20% 的数据 n≤3 。
对于 40% 的数据 n≤5 。
对于 60% 的数据 n≤8 。
对于 80% 的数据 n≤1000 。
对于 100% 的数据 n≤100000 且 m≤min(200000,n∗(n−1)/2) 。
样例解释
选择边 1−2,1−3,2−3,3−4 ,能组成美妙的仙人掌,且不存在其它美妙仙人掌有超过 4 条边。
solution
考试的时候一脸懵逼,woc这什么东西。
于是开始想各种奇怪的骗分,
n≤5 好像也可以手玩。
就这样活生生把前 40 分搞成了提交答案题。
最后脑洞大开,强行给每条边加一个负边权,跑一遍spfa,然后找到第一个入队次数超过 n 的节点,统计和这个点有关系的边数。
结果全天下都是 fuck
zyh输出
n
得了
下面才是题解
观察美丽的仙人掌的定义,发现编号为 i 与
i+1 的点之间必存在一条边问题转化成有若干区间,求最多的区间,使得区间之间没有重叠和覆盖。
然后贪心或者dp都可以。
看不懂吗?Emmm
我也看不懂
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100010
template<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;
}