2022 exercise 1

链接:登录—专业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;

    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值