链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
A珂朵莉的无向图 【多源最短路问题】
题目描述
珂朵莉给了你一个无向图,每次查询给t个点以及一个常数s,求有多少个图中的点p满足以下条件:能在给出的t个点中找出一个点y,满足p与y之间的最短距离≤s
输入描述:
第一行三个数表示n,m,q 接下来m行每行两个数u,v表示有一条长度为1的边位于u和v两个点之间 接下来 2q 行表示询问 每次询问第一行输入两个数t,s 第二行读入t个数,表示t个特殊点
输出描述:
q行,每行一个数表示答案
示例1
输入
复制5 6 6 2 3 1 3 2 5 1 3 3 2 2 5 1 1 3 1 1 1 1 4 1 1 2 5 1 4 1 1 4 5
5 6 6 2 3 1 3 2 5 1 3 3 2 2 5 1 1 3 1 1 1 1 4 1 1 2 5 1 4 1 1 4 5
输出
复制3 2 4 3 4 4
3 2 4 3 4 4
说明
n,m,q<= 5000 ,Σt<= 500000, s <= 109
本题边长是1,但是起点很多,算是一个多源最短路问题
多源最短路问题除了Floyd,其实还有多源BFS(边长为1)
在yxc的课acwing 173矩阵距离就是了
注意:
1.写惯了迪杰
喜欢把dis memset(dis,0x3f,sizeof dis)
但是到了BFS
一定要回过神来 memset(dis,-1,sizeof dis)
设置成-1也就不用开st[]了
2.写惯了迪杰,
入队最外层的判断条件
喜欢写成 if(dis[j]<dis[t]+w[i])
但是到了BFS
一定要回过神来:
第一个判断条件一定是判断有没有访问过
if(dis[]==-1) //如果没访问过
不需要用dis[j]<dis[t]+w[i]
这样的判断
多源BFS的思想
可以看y总的课,假设有一个虚拟源点,把所有与虚拟源点
距离为0的点入队(也就是所有起点)
一个必须记住的点
1.迪杰是出队后这个点的最小值才定下来也就是不变
2.而多源BFS是入队这个点的最小值就定下来了
所以我们的cnt统计个数要放在所有入队的位置
第二个重要的点
在所有起点的入队的时候也要判断是否访问过
就是在这里WA了
#include <bits/stdc++.h>
using namespace std;
int n,m,q;
const int N=1e5+10,M=2e5+10;
int e[M],ne[M],h[N],w[M],idx;
int dis[N];
int t,s;
int cnt;
queue<int> qq;
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=1;
h[a]=idx++;
}
int bfs()
{
//int cnt=0;
while(!qq.empty())
{
int t=qq.front();
qq.pop();
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dis[j]==-1)
{
dis[j]=dis[t]+1;
if(dis[j]<=s)
{
qq.push(j);
cnt++;
}
}
}
}
return cnt;
}
int main()
{
cin>>n>>m>>q;
memset(h,-1,sizeof h);
while(m--)
{
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
while(q--)
{
cin>>t>>s;
cnt=0;
memset(dis,-1,sizeof dis);
for(int i=1;i<=t;i++)
{
int cur;
cin>>cur;
if(dis[cur]==-1)
{
qq.push(cur);
dis[cur]=0;
cnt++;
}
}
cout<<bfs()<<endl;
}
}
D.旅行 【迪杰】
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
小z放假了,准备到RRR城市旅行,其中这个城市有N个旅游景点。小z时间有限,只能在三个旅行景点进行游玩。小明租了辆车,司机很善良,说咱不计路程,只要你一次性缴费足够,我就带你走遍RRR城。
小z很开心,直接就把钱一次性缴足了。然而小z心机很重,他想选择的路程尽量长。
然而司机也很聪明,他每次从一个点走到另外一个点的时候都走最短路径。
你能帮帮小z吗?
需要保证这三个旅行景点一个作为起点,一个作为中转点一个作为终点。(一共三个景点,并且需要保证这三个景点不能重复).
输入描述:
本题包含多组输入,第一行输入一个整数t,表示测试数据的组数 每组测试数据第一行输入两个数N,M表示RRR城一共有的旅游景点的数量,以及RRR城中有的路的数量。 接下来M行,每行三个数,a,b,c表示从a景点和b景点之间有一条长为c的路 t<=40 3<=N,M<=1000 1<=a,b<=N 1<=c<=100
输出描述:
每组数据包含一行,输出一个数,表示整条路程的路长。 如果找不到可行解,输出-1.
示例1
输入
复制4 7 7 1 2 100 2 3 100 1 4 4 4 5 6 5 6 10 1 6 4 6 7 8 7 3 1 2 1 1 3 1 1 3 2 7 3 1 2 1 3 4 1 5 6 1 8 9 1 2 1 2 3 1 3 4 1 4 1 1 4 5 1 5 6 1 6 7 1 7 8 1 8 5 1
4 7 7 1 2 100 2 3 100 1 4 4 4 5 6 5 6 10 1 6 4 6 7 8 7 3 1 2 1 1 3 1 1 3 2 7 3 1 2 1 3 4 1 5 6 1 8 9 1 2 1 2 3 1 3 4 1 4 1 1 4 5 1 5 6 1 6 7 1 7 8 1 8 5 1
输出
复制422 3 -1 9
422 3 -1 9
说明
请注意这是一个稀疏图.
题意
虽然有很多点,但只能保证一定经过三个点
一个起点,一个终点,一个中转点
解题策略
1.此题有点类似于三元组问题,
而三元组问题是先考虑中间点的,所以
我们此处也优先考虑中转点
2.以所有中转点为起点,用迪杰算法
求出每个以中转点的为起点的,第一长和
第二长距离,比较哪个距离和最大
3.第一长和第二长距离就是中转点到起点和终点的距离
注意
1.此处范围大,如果dis[][]是二维的
在main里memset(dis,0x3f,sizeof dis)会超时(教训!!!!)
2.要在main里面向迪杰传入 dis[i] (第一维)
然后在迪杰中 memset(dis,0x3f,sizeof N*4)
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e3+10,M=2e3+10;
const int INF=0x3f3f3f3f;
int e[M],ne[M],h[N],w[M],idx;
int dis[N][N];
bool st[N];
int n,m;
void add(int a,int b,int c)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=c;
h[a]=idx++;
}
void dij(int ori,int dis[])
{
memset(st,false,sizeof st);
memset(dis,0x3f,N*4);
dis[ori]=0;
priority_queue<PII,vector<PII>,greater<PII>> q;
q.push({0,ori});
while(!q.empty())
{
auto t=q.top();
q.pop();
int distance=t.first,u=t.second;
if(st[u]) continue;
st[u]=true;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(dis[j]>distance+w[i])
{
dis[j]=distance+w[i];
q.push({dis[j],j});
}
}
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
memset(h,-1,sizeof h);
idx=0;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
for(int i=1;i<=n;i++)
dij(i,dis[i]);
int MAXV=-1;
for(int i=1;i<=n;i++)
{
int MAXV1=-1,MAXV2=-1;
for(int j=1;j<=n;j++)
{
if(j==i) continue;
if(MAXV1<dis[i][j]&&dis[i][j]<INF/2)
{
MAXV2=MAXV1;
MAXV1=dis[i][j];
}
else if(dis[i][j]>MAXV2&&dis[i][j]<INF/2)
{
MAXV2=dis[i][j];
}
}
//cout<<MAXV1<<" "<<MAXV2<<endl;
if(MAXV1!=-1&&MAXV2!=-1)
MAXV=max(MAXV,MAXV1+MAXV2);
}
if(MAXV>INF/2) cout<<"-1"<<endl;
else cout<<MAXV<<endl;
}
}
E.字符串问题 【字符串哈希+二分答案】
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
有一个字符串 让你找到这个字符串 S 里面的子串T 这个子串 T 必须满足即使这个串的前缀 也是这个
串的后缀 并且 在字符串中也出现过一次的(提示 要求满足前后缀的同时也要在字符串中出现一次 只是前后缀可不行 输出最长满足要求字符串)
输入描述:
给出一个字符串 长度 1 到 1e6 全部是小写字母
输出描述:
如果找的到就输出这个子串T 如果不行就输出 Just a legend
示例1
输入
复制fixprefixsuffix
fixprefixsuffix
输出
复制fix
fix
示例2
输入
复制abcdabc
abcdabc
输出
复制Just a legend
Just a legend
题意:
一个字符串中前缀与后缀相同
并且中间也能找到个子串,与这个前缀和后缀相同
求前缀=后缀=中间子串时
的最大长度
注意
1.中间子串,不能覆盖第一位和最后一位
比如aaaaaa
它的最长前后缀和中间子串是 aaaa
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=1e6+10;
const int P=131;
typedef pair<int,ULL> PII;
string s;
ULL p[N];
ULL h[N];
int ll=0;
vector<PII> lens;
ULL get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];
}
bool check(int mid)
{
ULL hash=lens[mid].second;
int len=lens[mid].first;
for(int i=2;i<=ll-1;i++)
{
if(i+len-1<=ll-1&&get(i,i+len-1)==hash)
return true;
}
return false;
}
int main()
{
cin>>s;
p[0]=1;
//nt ll=0;
for(int i=0;i<s.size();i++)
{
p[i+1]=p[i]*P;
h[i+1]=h[i]*P+s[i];
}
//cout<<l<<endl;
ll=s.size();
for(int len=1;len<=ll;len++)
{
if(get(1,1+len-1)==get(ll-len+1,ll))
{
lens.push_back({len,get(1,1+len-1)});
}
}
int l=-1,r=lens.size();
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
if(l==-1)
{
cout<<"Just a legend"<<endl;
return 0;
}
for(int i=0;i<lens[l].first;i++)
cout<<s[i];
cout<<endl;
}
兔子的名字 【双指针算法】
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
兔子发现序列的名字都是数字,实在太无聊了,于是兔子开始研究兔子的名字。
现在兔子手上有 n 个名字 Ti 和 m 个可爱词汇Sj,兔子对每一个名字 Ti 定义了一个可爱度,如果 Ti 中出现了一个可爱的单词 Sj,那么 Ti 就有 1 点可爱值,最后的总可爱值就是 Ti 的可爱度,这里的出现指 Sj 是 Ti 的子序列。
例如 abc 是 aebdc 的子序列,abc 也是 abcd 的子序列。
现在兔子想知道每一个名字的可爱度。
输入描述:
第 1 行两个整数 n 和 m,表示名字个数和可爱词汇个数。 接下来 n 行,第 i 行是字符串 Ti ,表示兔子手里的名字。 再接下来 m 行,每行一个字符串 Sj ,表示兔子手里的可爱词汇。
输出描述:
输出共 n 行,每行一个整数,表示每一个名字的可爱度。
示例1
输入
复制5 3 Bunny Rabbit TuZi MianZi Sunny uny i a
5 3 Bunny Rabbit TuZi MianZi Sunny uny i a
输出
复制1 2 1 2 1
1 2 1 2 1
说明
Bunny 中有 uny 这个可爱词汇 Rabbit 中有 i 和 a 这个可爱词汇 TuZi 中有 i 这个可爱词汇 MianZi中有 i 和 a 这个可爱词汇 Sunny中有 uny 这个可爱词汇 与题目无关: //Tuzi(兔子)->MianZi(免子)
备注:
对于 40%的数据 1 ≤ n ≤ 100 对于 100%的数据 1 ≤ n ≤ 1000,1 ≤ m ≤ 100,1 ≤| Ti |≤ 100,1 ≤|Si|≤ 30
|s| 表示 s 的长度
请注意,字符串区分大小写
#include <bits/stdc++.h>
using namespace std;
const int N=1e3+10,M=1e2+10;
char t[N][105];
char s[M][35];
int n,m;
bool check(char a[],char b[])
{
/*
for(int i=0;a[i]!='\0';i++)
cout<<a[i];
cout<<endl;
for(int i=0;b[i]!='\0';i++)
cout<<b[i];
cout<<endl;*/
int j=0;
bool flag=false;
for(int i=0;a[i]!='\0';i++)
{
if(a[i]==b[j])
{
j++;
if(b[j]=='\0')
{
flag=true;
}
}
}
return flag;
}
int main()
{
//int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
{
scanf("%s",t[i]);
}
for(int i=0;i<m;i++)
{
scanf("%s",s[i]);
}
for(int i=0;i<n;i++)
{
int sum=0;
for(int j=0;j<m;j++)
{
if(check(t[i],s[j]))
sum++;
}
cout<<sum<<endl;
}
return 0;
}