思路:用vector数组存每个点的课链接点,一个数字存两点间的路段属于哪个公司
tmp是暂时存现在路线,ans是保存最优路线
vis是记录该点有没有访问过,题目说存在圈,所以用其防止重复点
dfs暴搜,如果现在路线好于历史最优路线,则ans=tmp
tmp和ans存点的时候按:点——公司——点.......——公司——点 的方式存,打印是先提取第一个,之后两个两个遍历,取公司、点,末点就是下一个始点
//还原现场,非公司vis #include<bits/stdc++.h> using namespace std; const int N=1e4+10,Max=0x3f3f3f3f; int cpy[N][N],n,m,k,a,b; vector<int> ma[N],tmp,ans; int mincs,minps; int vis[N]; void dfs(int p,int ps,int c,int cs){//当前站点,当前站点sum,当前公司no,当前转公司sum if(p==b){ tmp.push_back(c), tmp.push_back(p);//公司,当前站点 if(ps<minps) ans=tmp, minps=ps, mincs=cs; else if(ps==minps&&cs<mincs) ans=tmp, minps=ps, mincs=cs; tmp.pop_back(), tmp.pop_back(); return ; } for(auto i:ma[p]){ if(vis[i]==0){ if(cpy[p][i]!=c){//公司不同 if(c!=-1) tmp.push_back(c), tmp.push_back(p);//pre公司,和下一站点(中转站) vis[i]=1; dfs(i,ps+1,cpy[p][i],cs+1); if(c!=-1)tmp.pop_back(), tmp.pop_back(), vis[i]=0;//还原现场 }else{ vis[i]=1; dfs(i,ps+1,c,cs); vis[i]=0; } } } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&m,&a); for(int j=1;j<m;j++){ scanf("%d",&b); ma[a].push_back(b), ma[b].push_back(a), cpy[a][b]=cpy[b][a]=i; a=b; } } scanf("%d",&k); for(int i=0;i<k;i++){ scanf("%d%d",&a,&b); mincs=minps=Max; memset(vis,0,sizeof vis); ans.clear(), tmp.clear(); vis[a]=1, tmp.push_back(a); dfs(a,1,-1,0); if(ans.size()){ printf("%d\n",minps-1); int f=*ans.begin(),e,cpyno; ans.erase(ans.begin()); while(ans.size()){ cpyno=*ans.begin(); ans.erase(ans.begin()); e=*ans.begin(); ans.erase(ans.begin()); printf("Go by the line of company #%d from %04d to %04d.\n",cpyno,f,e); f=e; } }else{ puts("Sorry, no line is available."); } } return 0; }
这里完整的还原现场十分重要
if(c!=-1) tmp.push_back(c), tmp.push_back(p);//pre公司,和下一站点(中转站) vis[i]=1; dfs(i,ps+1,cpy[p][i],cs+1); if(c!=-1)tmp.pop_back(), tmp.pop_back(), vis[i]=0;//还原现场
这里由于第一个只给站点,没有线段不存在公司,所以先传c=-1
当时下面没有if(c!=0) 调了我两天非公司忘记设vis