【缩点 强连通分量】P1262 间谍网络|普及+

【缩点 强连通分量】P1262 间谍网络|普及+

本文涉及知识点

C++图论 缩点 强连通分量

P1262 间谍网络

题目描述

由于外国间谍的大量渗入,国家安全正处于高度的危机之中。如果 A 间谍手中掌握着关于 B 间谍的犯罪证据,则称 A 可以揭发 B。有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报。所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子。因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。

我们的反间谍机关提供了一份资料,包括所有已知的受贿的间谍,以及他们愿意收受的具体数额。同时我们还知道哪些间谍手中具体掌握了哪些间谍的资料。假设总共有 n n n 个间谍( n n n 不超过 3000 3000 3000),每个间谍分别用 1 1 1 到 3000 3000 3000 的整数来标识。

请根据这份资料,判断我们是否有可能控制全部的间谍,如果可以,求出我们所需要支付的最少资金。否则,输出不能被控制的一个间谍。

输入格式

第一行只有一个整数 n n n。

第二行是整数 p p p。表示愿意被收买的人数, 1 ≤ p ≤ n 1\le p\le n 1≤p≤n。

接下来的 p p p 行,每行有两个整数,第一个数是一个愿意被收买的间谍的编号,第二个数表示他将会被收买的数额。这个数额不超过 20000 20000 20000。

紧跟着一行只有一个整数 r r r, 1 ≤ r ≤ 8000 1\le r\le8000 1≤r≤8000。然后 r r r 行,每行两个正整数,表示数对 ( A , B ) (A, B) (A,B), A A A 间谍掌握 B B B 间谍的证据。

输出格式

如果可以控制所有间谍,第一行输出 YES,并在第二行输出所需要支付的贿金最小值。否则输出 NO,并在第二行输出不能控制的间谍中,编号最小的间谍编号。

输入输出样例 #1

输入 #1

3 2 1 10 2 100 2 1 3 2 3 

输出 #1

YES 110 

输入输出样例 #2

输入 #2

4 2 1 100 4 200 2 1 2 3 4 

输出 #2

NO 3 

缩点 无向图寻找环

简化: 存在某个节点能访问所有节点,如节点0。

m_vTime[cur]记录cur的DFS序。未处理的节点,m_vTime[cur]等于-1。已经处理的节点,DFS直接返回,保证每个点只DFS一次。
性质一:cur直接或间接DFS到的任何节点node,m_vTime[cur] < m_vTime[node]。则node一定是cur的后代。且DFS(cur)结束时,cur所有的后代都访问结束。
vPar[cur]记录cur的父节点,由于只会DFS一次,除根节点外,故只有一个父节点。
一,树边。 父节点 → 子节点 父节点\rightarrow 子节点 父节点→子节点。DSF序小指向DFS序大。
二,前向边。 祖先 → 后代 祖先\rightarrow 后代 祖先→后代,全部删除不影响环。任意祖先通过树边可以到达任意后代。DSF序小指向DFS序大。
三,后向边(回边)。 后代 → 祖先 后代\rightarrow 祖先 后代→祖先DSF序大指向DFS序小。
四,横向边。其它边。 D S F 序大指向 D F S 序小。 ∗ ∗ 性质二 ∗ ∗ :任意环一定不包括横边,令横边 DSF序大指向DFS序小。 **性质二**:任意环一定不包括横边,令横边 DSF序大指向DFS序小。∗∗性质二∗∗:任意环一定不包括横边,令横边u\rightarrow v$,由于是环v也能访问u,且DFS序小,根据性质一是回边。
性质三:只有树边无法构成环,DFS只会越来越大;同理,只有回边,也无环。环必须有树边和回边。
性质四:一个环有多条回边,可以拆分若干个只有一条回边的环(我简称基础环)。
时间复杂度:O(边数+点数)

环的合并 多个环有共同点则是一个强连通区域

性质五:基础环的回边是 u → v u\rightarrow v u→v,长度是len,则构成的点是: u 的 [ 0 到 l e n − 1 ] 级祖先 u的[0到len-1]级祖先 u的[0到len−1]级祖先。
极端情况下,长度n的链,回边 n × ( n − 1 ) 2 \frac{n\times(n-1)}{2} 2n×(n−1)​,环长n。时间复杂度:O(nnn)
cnt[cur]=i,表示节点cur的0到i-1级祖先构成环。如果cur有多个环,取cnt[cur]最大的。
m_vSortToNode[i] 记录DFS序为i的节点。
通过cur逆序访问m_vSortToNode。如果cur存在父节点,且cnt[cur] > 0{uf.连通cur和父节点。MaxSelf(cnt[父节点],cur[cur]-1)}
uf中的连通区域可以缩成一个点。
时间复杂度:O(n)

不知道是否有点连通所有点

DFS(0…N-1)。由于环上任意一点,可以访问环上其它点,故DFS到环上任意一点,则可以DFS到整个环。即环不会分成两部分。
注意:DFS的时候,生成各点层次、父节点、各节点DFS序、各DFS序对应节点。
DFS(当前节点,父节点,层次)

缩点

如果存在环,缩成点,收买价格取最小值。
出掉自环,重边出不出都可以。
所有入度为0的间谍都要收买。

如果存在无法收买的间谍

缩点后的有向图G1的临接表为neiBo1,can记录所有能够收买的间谍组(一个间谍对应原图的一个点,一组间谍对应G1的一个点)。
以can为0层,求层次。层次-1的间谍组无法收买。求无法收买的间谍组的间谍的最小编号。

代码

核心代码

#include<iostream>#include<sstream>#include<vector>#include<map>#include<unordered_map>#include<set>#include<unordered_set>#include<string>#include<algorithm>#include<functional>#include<queue>#include<stack>#include<iomanip>#include<numeric>#include<math.h>#include<climits>#include<assert.h>#include<cstring>#include<list>#include<array>#include<bitset>usingnamespace std;template<classT1,classT2> std::istream&operator>>(std::istream& in, pair<T1, T2>& pr){ in >> pr.first >> pr.second;return in;}template<classT1,classT2,classT3> std::istream&operator>>(std::istream& in, tuple<T1, T2, T3>& t){ in >> get<0>(t)>> get<1>(t)>> get<2>(t);return in;}template<classT1,classT2,classT3,classT4> std::istream&operator>>(std::istream& in, tuple<T1, T2, T3, T4>& t){ in >> get<0>(t)>> get<1>(t)>> get<2>(t)>> get<3>(t);return in;}template<classT1,classT2,classT3,classT4,classT5,classT6,classT7> std::istream&operator>>(std::istream& in, tuple<T1, T2, T3, T4,T5,T6,T7>& t){ in >> get<0>(t)>> get<1>(t)>> get<2>(t)>> get<3>(t)>> get<4>(t)>> get<5>(t)>> get<6>(t);return in;}template<classT=int> vector<T>Read(){int n; cin >> n; vector<T>ret(n);for(int i =0; i < n; i++){ cin >> ret[i];}return ret;}template<classT=int> vector<T>ReadNotNum(){ vector<T> ret; T tmp;while(cin >> tmp){ ret.emplace_back(tmp);if('\n'== cin.get()){break;}}return ret;}template<classT=int> vector<T>Read(int n){ vector<T>ret(n);for(int i =0; i < n; i++){ cin >> ret[i];}return ret;}template<int N =1'000'000>classCOutBuff{public:COutBuff(){ m_p = puffer;}template<classT>voidwrite(T x){int num[28], sp =0;if(x <0)*m_p++='-', x =-x;if(!x)*m_p++=48;while(x) num[++sp]= x %10, x /=10;while(sp)*m_p++= num[sp--]+48;AuotToFile();}voidwritestr(constchar* sz){strcpy(m_p, sz); m_p +=strlen(sz);AuotToFile();}inlinevoidwrite(char ch){*m_p++= ch;AuotToFile();}inlinevoidToFile(){fwrite(puffer,1, m_p - puffer,stdout); m_p = puffer;}~COutBuff(){ToFile();}private:inlinevoidAuotToFile(){if(m_p - puffer > N -100){ToFile();}}char puffer[N],* m_p;};template<int N =1'000'000>classCInBuff{public:inlineCInBuff(){}inline CInBuff<N>&operator>>(char& ch){FileToBuf();while(('\r'==*S)||('\n'==*S)||(' '==*S)){ S++;}//忽略空格和回车 ch =*S++;return*this;}inline CInBuff<N>&operator>>(int& val){FileToBuf();intx(0),f(0);while(!isdigit(*S)) f |=(*S++=='-');while(isdigit(*S)) x =(x <<1)+(x <<3)+(*S++^48); val = f ?-x : x; S++;//忽略空格换行 return*this;}inline CInBuff&operator>>(longlong& val){FileToBuf();longlongx(0);intf(0);while(!isdigit(*S)) f |=(*S++=='-');while(isdigit(*S)) x =(x <<1)+(x <<3)+(*S++^48); val = f ?-x : x; S++;//忽略空格换行return*this;}template<classT1,classT2>inline CInBuff&operator>>(pair<T1, T2>& val){*this>> val.first >> val.second;return*this;}template<classT1,classT2,classT3>inline CInBuff&operator>>(tuple<T1, T2, T3>& val){*this>> get<0>(val)>> get<1>(val)>> get<2>(val);return*this;}template<classT1,classT2,classT3,classT4>inline CInBuff&operator>>(tuple<T1, T2, T3, T4>& val){*this>> get<0>(val)>> get<1>(val)>> get<2>(val)>> get<3>(val);return*this;}template<classT=int>inline CInBuff&operator>>(vector<T>& val){int n;*this>> n; val.resize(n);for(int i =0; i < n; i++){*this>> val[i];}return*this;}template<classT=int> vector<T>Read(int n){ vector<T>ret(n);for(int i =0; i < n; i++){*this>> ret[i];}return ret;}template<classT=int> vector<T>Read(){ vector<T> ret;*this>> ret;return ret;}private:inlinevoidFileToBuf(){constint canRead = m_iWritePos -(S - buffer);if(canRead >=100){return;}if(m_bFinish){return;}for(int i =0; i < canRead; i++){ buffer[i]= S[i];//memcpy出错 } m_iWritePos = canRead; buffer[m_iWritePos]=0; S = buffer;int readCnt =fread(buffer + m_iWritePos,1, N - m_iWritePos,stdin);if(readCnt <=0){ m_bFinish =true;return;} m_iWritePos += readCnt; buffer[m_iWritePos]=0; S = buffer;}int m_iWritePos =0;bool m_bFinish =false;char buffer[N +10],* S = buffer;};classCNeiBo{public:static vector<vector<int>>Two(int n,const vector<pair<int,int>>& edges,bool bDirect,int iBase =0){ vector<vector<int>>vNeiBo(n);for(constauto&[i1, i2]: edges){ vNeiBo[i1 - iBase].emplace_back(i2 - iBase);if(!bDirect){ vNeiBo[i2 - iBase].emplace_back(i1 - iBase);}}return vNeiBo;}static vector<vector<int>>Two(int n,const vector<vector<int>>& edges,bool bDirect,int iBase =0){ vector<vector<int>>vNeiBo(n);for(constauto& v : edges){ vNeiBo[v[0]- iBase].emplace_back(v[1]- iBase);if(!bDirect){ vNeiBo[v[1]- iBase].emplace_back(v[0]- iBase);}}return vNeiBo;}static vector<vector<std::pair<int,int>>>Three(int n, vector<vector<int>>& edges,bool bDirect,int iBase =0){ vector<vector<std::pair<int,int>>>vNeiBo(n);for(constauto& v : edges){ vNeiBo[v[0]- iBase].emplace_back(v[1]- iBase, v[2]);if(!bDirect){ vNeiBo[v[1]- iBase].emplace_back(v[0]- iBase, v[2]);}}return vNeiBo;}static vector<vector<std::pair<int,int>>>Three(int n,const vector<tuple<int,int,int>>& edges,bool bDirect,int iBase =0){ vector<vector<std::pair<int,int>>>vNeiBo(n);for(constauto&[u, v, w]: edges){ vNeiBo[u - iBase].emplace_back(v - iBase, w);if(!bDirect){ vNeiBo[v - iBase].emplace_back(u - iBase, w);}}return vNeiBo;}static vector<vector<int>>Mat(vector<vector<int>>& neiBoMat){ vector<vector<int>>neiBo(neiBoMat.size());for(int i =0; i < neiBoMat.size(); i++){for(int j = i +1; j < neiBoMat.size(); j++){if(neiBoMat[i][j]){ neiBo[i].emplace_back(j); neiBo[j].emplace_back(i);}}}return neiBo;}};classCBFSLeve{public:static vector<int>Leve(const vector<vector<int>>& neiBo, vector<int> start){ vector<int>leves(neiBo.size(),-1);for(constauto& s : start){ leves[s]=0;}for(int i =0; i < start.size(); i++){for(constauto& next : neiBo[start[i]]){if(-1!= leves[next]){continue;} leves[next]= leves[start[i]]+1; start.emplace_back(next);}}return leves;}template<classNextFun>static vector<int>Leve(int N, NextFun nextFun, vector<int> start){ vector<int>leves(N,-1);for(constauto& s : start){ leves[s]=0;}for(int i =0; i < start.size(); i++){auto nexts =nextFun(start[i]);for(constauto& next : nexts){if(-1!= leves[next]){continue;} leves[next]= leves[start[i]]+1; start.emplace_back(next);}}return leves;}static vector<vector<int>>LeveNodes(const vector<int>& leves){constint iMaxLeve =*max_element(leves.begin(), leves.end()); vector<vector<int>>ret(iMaxLeve +1);for(int i =0; i < leves.size(); i++){ ret[leves[i]].emplace_back(i);}return ret;};static vector<int>LeveSort(const vector<int>& leves){constint iMaxLeve =*max_element(leves.begin(), leves.end()); vector<vector<int>>leveNodes(iMaxLeve +1);for(int i =0; i < leves.size(); i++){ leveNodes[leves[i]].emplace_back(i);} vector<int> ret;for(constauto& v : leveNodes){ ret.insert(ret.end(), v.begin(), v.end());}return ret;};};classCSCCTarjan{public:CSCCTarjan(vector<vector<int>>& neiBo):m_neiBo(neiBo){constint N = neiBo.size(); m_vTime.assign(N,-1); m_vBack.assign(N,-1); m_vIsStack.assign(N,false);for(int i =0; i < N; i++){DFS(i);}} vector<vector<int>> m_sccs;protected:voidDFS(int cur){if(-1!= m_vTime[cur]){return;} m_vTime[cur]= m_vBack[cur]= m_iTimes++; m_vIsStack[cur]=true; m_sta.emplace(cur);for(constauto& next : m_neiBo[cur]){if(-1== m_vTime[next]){DFS(next); m_vBack[cur]=min(m_vBack[cur], m_vBack[next]);}elseif(m_vIsStack[next]){ m_vBack[cur]=min(m_vBack[cur], m_vTime[next]);}}if(m_vTime[cur]!= m_vBack[cur]){return;} vector<int> scc;while(m_sta.size()){auto u = m_sta.top(); m_sta.pop(); scc.emplace_back(u); m_vIsStack[u]=false;if(cur == u){break;}} m_sccs.emplace_back(scc);} vector<vector<int>>& m_neiBo;int m_iTimes =0; vector<int> m_vTime, m_vBack; vector<bool> m_vIsStack; stack<int> m_sta;};classSolution{public: pair<bool,int>Ans(constint N, vector<pair<int,int>>& vNoMoney, vector<pair<int,int>>& edge){ vector<int>need(N, INT_MAX /2);for(constauto&[v, Mon]: vNoMoney){ need[v -1]= Mon;}auto neiBo =CNeiBo::Two(N, edge,true,1); CSCCTarjan sp(neiBo); vector<vector<int>>& sccs = sp.m_sccs; vector<int>ptNew(N);iota(ptNew.begin(), ptNew.end(),0); vector<int> v0;for(auto& v : sccs){nth_element(v.begin(), v.begin(), v.end()); v0.emplace_back(v[0]);for(int i =1; i < v.size(); i++){ need[v[0]]=min(need[v[0]], need[v[i]]); ptNew[v[i]]= v[0];}}sort(v0.begin(), v0.end()); vector<int>in(N); vector<vector<int>>neiBo1(N);for(int i =0; i < N; i++){constint p1 = ptNew[i];for(constauto& next : neiBo[i]){constint p2 = ptNew[next];if(p1 == p2){continue;}//忽略自环 in[p2]++; neiBo1[p1].emplace_back(p2);}}int ans =0;bool bCanAll =true;for(auto& n : v0){if(0!= in[n]){continue;}if(need[n]>= INT_MAX /2){ bCanAll =false;break;} ans += need[n];}if(bCanAll){return{true,ans };} vector<int> can;for(auto& n : v0){if(need[n]>= INT_MAX /2){continue;} can.emplace_back(n);}auto leves =CBFSLeve::Leve(neiBo1, can);int iMin = INT_MAX /2;for(constauto& n : v0){if(-1== leves[n]){return{false,n +1};}}return{true,-1};}};intmain(){#ifdef_DEBUGfreopen("a.in","r",stdin);#endif// DEBUG  ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob;int n; cin >> n;auto noMoney = Read<pair<int,int>>();auto edge = Read<pair<int,int>>();#ifdef_DEBUG printf("N=%d",n);Out(noMoney,",noMoney=");Out(edge,",edge=");#endif// DEBUG auto res =Solution().Ans(n,noMoney,edge); cout <<(res.first ?"YES":"NO")<<"\n"; cout << res.second <<"\n";return0;};

单元测试

int N; vector<pair<int,int>> noMoney, edge;TEST_METHOD(TestMethod01){ N =3, noMoney ={{1,10},{2,100}}, edge ={{1,3},{2,3}};auto res =Solution().Ans(N, noMoney,edge);AssertEx({true,110}, res);}TEST_METHOD(TestMethod02){ N =4, noMoney ={{1,100},{4,200}}, edge ={{1,2},{3,4}};auto res =Solution().Ans(N, noMoney, edge);AssertEx({false,3}, res);}TEST_METHOD(TestMethod03){ N =4, noMoney ={{1,2},{2,2},{3,3},{4,4}}, edge ={{1,2},{1,3},{1,4},{4,3},{3,2},{2,1}};auto res =Solution().Ans(N, noMoney, edge);AssertEx({true,2}, res);}TEST_METHOD(TestMethod05){ N =9, noMoney ={{1,100},{9,10000}}, edge ={{1,3},{4,3},{2,4},{2,1},{8,5},{8,7},{5,6},{7,6},{4,5}};auto res =Solution().Ans(N, noMoney, edge);AssertEx({false,2}, res);}TEST_METHOD(TestMethod06){ N =4, noMoney ={{4,10}}, edge ={{3,2},{3,1}};auto res =Solution().Ans(N, noMoney, edge);AssertEx({false,1}, res);}TEST_METHOD(TestMethod04){int n =5;// Number of nodes vector<vector<int>>adj(n);// Example graph adj[0]={1}; adj[1]={2}; adj[2]={0,3}; adj[3]={4}; adj[4]={3}; vector<vector<int>> sccs =CShrinkPoint().kosaraju(adj, n);}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步ZEEKLOG学院,听白银讲师(也就是鄙人)的讲解。
https://edu.ZEEKLOG.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.ZEEKLOG.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

Read more

AI 生成的 UI 太丑?3 步让你的前端秒变高级感

AI 生成的 UI 太丑?3 步让你的前端秒变高级感

🚀 AI 生成的 UI 太丑?3 步让你的前端秒变高级感 你是不是也遇到过这种情况:满心期待地用 AI 生成一个前端页面,结果得到的是一个土到掉渣的蓝紫色界面,丑到自己都看不下去?🤦‍♂️ 别担心,你不是一个人!这是目前 90% 开发者使用 AI 写前端时都会遇到的痛点。 好消息是,经过一番研究和实践,我们发现了一些有效的方法!通过几个简单的技巧,不需要手写任何 CSS,就能让 AI 帮你生成媲美专业设计师的 UI 界面。 今天就手把手教你 3 步搞定,让 AI 彻底告别 “AI 味”! 🧪 实验准备 工具准备 想要跟着实验,你需要准备: 1. Claude Code (2.0.55) 底层模型是 Minimax-M2

By Ne0inhk

从2025看2026前端发展趋势

🎨 从2025看2026前端发展趋势 一、📌 核心前言(2025铺垫→2026展望) 2025年前端行业已完成“基础成熟化”:Vue3、React18成为主流,TypeScript全面普及,工程化流程趋于完善,AI工具开始渗透开发环节,但也暴露了痛点——开发效率不均衡、跨端体验不一致、AI与业务结合浅显、性能优化门槛高。 ✨ 核心趋势:2026年前端将从「基础成熟」走向「深度融合」,重点围绕「AI原生开发」「跨端统一」「性能极致」「工程化提效」四大方向突破,同时Node.js等底层工具的升级(如2026年Node.js新特性)将进一步推动前端向全栈化、平台化转型。 二、✍️ 五大核心趋势(手绘重点·结合2025现状) 1. AI原生开发:从“辅助工具”到“核心生产力” 🤖(最重磅) (1)2025现状 2025年,前端AI工具多为“辅助层面”

By Ne0inhk

Xilinx用户必看!复旦微FMQL45T900与ZYNQ7045对比测评:性能差异与迁移指南

复旦微FMQL45T900深度实战:从Xilinx ZYNQ迁移的硬核指南与性能真相 如果你正在使用Xilinx ZYNQ系列,尤其是ZYNQ7045这个级别的器件,最近可能被供应链、成本或者某些不可控的外部因素搞得有点头疼。我身边不少做工业控制、通信处理和边缘计算的朋友,都在私下里讨论同一个话题:有没有一条靠谱的“备胎”路线?国产化的FMQL45T900频繁被提及,但它到底行不行?直接替换ZYNQ7045会不会掉进坑里?这篇文章,我想从一个实际项目迁移者的角度,抛开那些官方的参数对比,聊聊我们团队在真实项目中踩过的雷、趟过的河,以及最终拿到的性能数据。这不是一篇简单的参数罗列,而是一份聚焦于“如何安全落地”的实战迁移指南。 1. 架构透视:不仅仅是“ARM+FPGA”的简单对标 第一次拿到FMQL45T900的芯片手册时,我的第一反应是:这看起来和ZYNQ的PS+PL架构太像了。但深入下去,你会发现“形似而神不同”,这些差异恰恰是迁移成败的关键。 1.1 PS端:四核Cortex-A7的实战能力剖析 FMQL45T900的PS(处理系统)搭载了四核ARM Cortex-A

By Ne0inhk
圣塔克拉拉大学打造“算法育种师“:AI自动发现信息检索新方法

圣塔克拉拉大学打造“算法育种师“:AI自动发现信息检索新方法

圣塔克拉拉大学、沃尔玛全球技术公司等机构的研究团队于2026年2月18日发表了一项突破性研究,论文编号为arXiv:2602.16932v1,展示了如何让大语言模型像生物育种师一样,自动培育出更强大的信息检索算法。有兴趣深入了解的读者可以通过该编号查询完整论文。 当你在搜索引擎中输入关键词寻找信息时,背后有一套复杂的算法在决定哪些网页最适合你。这些算法就像图书管理员,需要从海量信息中挑选出最相关的内容。几十年来,这些"管理员"的工作方式主要依靠人类专家的经验和直觉来改进,就好比一代代图书管理员根据经验传授整理图书的技巧。 然而,这种依赖人工经验的方式存在明显局限。正如一个图书管理员再聪明,也很难同时掌握所有可能的图书分类方法一样,人类专家很难探索所有可能的算法改进方向。研究团队因此提出了一个大胆的想法:能否让AI像生物学家培育新品种一样,自动培育出更优秀的检索算法? 这项研究的核心成果是一个名为RankEvolve的系统。这个系统基于进化算法的原理,让候选算法像生物体一样进行"繁殖"和"进化"。研究团队从两个经典的检索算法开始,就像从两个优良品种开始育种一样,然后让大语言模型充

By Ne0inhk