总结
日期 | 事件 | 刷的题 |
---|---|---|
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.in | closest.out |
---|---|
3075 | 4066 |
6604 | 0 |
【输入输出样例2】
closest.in | closest.out |
---|---|
3000203 | 4244556 |
4562454 | 2655444 |
竖着看,每次输入有两个数
【限制】
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.in | ygas.out |
---|---|
abcd | 0 |
【输入输出样例2】
ygas.in | ygas.out |
---|---|
ababa | 3 |
【输入输出样例3】
ygas.in | ygas.out |
---|---|
zzz | 2 |
思路
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相遇的时间等于
取所有方案中的最小值,即为
代码(非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;
}