10.3 图的遍历
【例】A1013 Battle Over Cities (25 分)
ATTENTION
- 要添加的边的数量为图中的连通块数-1。
- 注意原图要进行多次查询,所以只能标记要删除的边,而不能对原图做修改!
并查集做法
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
const int maxn=1010;
int n,m,k;
int g[maxn][maxn]={0},fa[maxn];
bool vis[maxn];
int findFather(int a)
{
if(fa[a]==a) return a;
fa[a]=findFather(fa[a]); //路径压缩,否则超时
return fa[a];
}
void Union(int a,int b)
{
int faa=findFather(a);
int fab=findFather(b);
if(faa!=fab)
fa[faa]=fab;
return;
}
int main()
{
scanf("%d %d %d",&n,&m,&k);
for(int i=0;i<m;i++)
{
int c1,c2;
scanf("%d %d",&c1,&c2);
g[c1][c2]=1;
g[c2][c1]=1;
}
for(int h=0;h<k;h++)
{
for(int i=1;i<=n;i++)
fa[i]=i;
fill(vis,vis+maxn,0);
int tmp;
scanf("%d",&tmp);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j&&i!=tmp&&j!=tmp&&g[i][j]!=0)
Union(i,j);
int cnt=0;
for(int i=1;i<=n;i++)
if(i!=tmp&&fa[i]==i) cnt++;
printf("%d\n",cnt-1);
}
}
遍历做法
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
const int maxn=1010;
int n,m,k;
int g[maxn][maxn]={0};
int idx[maxn]={0};
bool vis[maxn]={false};
void dfs(int u,int cnt,int tmp)
{
vis[u]=true;
idx[u]=cnt;
for(int i=1;i<=n;i++)
if(i!=tmp&&g[u][i]==1&&vis[i]==false)
dfs(i,cnt,tmp);
}
int main()
{
scanf("%d %d %d",&n,&m,&k);
for(int i=0;i<m;i++)
{
int c1,c2;
scanf("%d %d",&c1,&c2);
g[c1][c2]=1;
g[c2][c1]=1;
}
for(int i=0;i<k;i++)
{
int tmp=-1;
fill(vis,vis+maxn,0);
fill(idx,idx+maxn,0);
scanf("%d",&tmp);
int cnt=1;
for(int j=1;j<=n;j++)
{
if(j!=tmp&&vis[j]==false)
{
dfs(j,cnt,tmp);
cnt++;
}
}
set<int> theTmp;
for(int i=1;i<=n;i++)
if(i!=tmp) theTmp.insert(idx[i]);
printf("%d\n",theTmp.size()-1);
}
return 0;
}
【例】1021 Deepest Root (25 分)
acyclic a.无环的
ATTENTION
- 这道题暴力进行
n
遍dfs
其实也能过。 - 求最大树高/树的直径: 从树中找出任意一点,求出与他距离最远的点
s
,再用同样的方法求出与s
距离最远的点t
,s-t
即为树的直径。 - 映射到本题即:如果只有一个连通分支,则先任选一个顶点进行一次dfs,记录下离起点最远的结点们。再从这些结点中选择一个进行dfs,得到离他最远的结点们,这两个结点们的集合就是所求。
- 【注】在求树高的时候,不能在循环内部h++,否则对于当前顶点,其邻接点的高度就不同了。
#include <cstdio>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
const int maxn=10010;
int n,layer[maxn];
bool vis[maxn]={false};
vector<int> st1,adj[maxn];
int maxh=0;
void dfs(int s,int h)
{
vis[s]=true;
if(h>maxh)
{
st1.clear();
st1.push_back(s);
maxh=h;
}
else if(h==maxh)
st1.push_back(s);
for(int i=0;i<adj[s].size();i++)
{
if(vis[adj[s][i]]==false)
{
dfs(adj[s][i],h+1); //注意这里不能h++
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int c1,c2;
scanf("%d %d",&c1,&c2);
adj[c1].push_back(c2);
adj[c2].push_back(c1);
}
int cnt=0;
for(int i=1;i<=n;i++)
{
if(vis[i]==false)
{
cnt++;
dfs(i,1);
}
}
if(cnt>1)
printf("Error: %d components",cnt);
else
{
set<int> ans;int tmp;
for(int i=0;i<st1.size();i++)
{
tmp=st1[0];
ans.insert(st1[i]);
}
st1.clear();
maxh=0;
fill(vis,vis+maxn,0);
dfs(tmp,1);
for(int i=0;i<st1.size();i++)
ans.insert(st1[i]);
for(auto it=ans.begin();it!=ans.end();it++)
printf("%d\n",*it);
}
}
暴力
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=10010;
int n,layer[maxn];
bool vis[maxn]={false};
vector<int> ans,adj[maxn];
void dfs(int s,int &max)
{
vis[s]=true;
for(int i=0;i<adj[s].size();i++)
{
int v=adj[s][i];
if(vis[v]==false)
{
layer[v]=layer[s]+1;
if(layer[v]>max) max=layer[v];
dfs(v,max);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int c1,c2;
scanf("%d %d",&c1,&c2);
adj[c1].push_back(c2);
adj[c2].push_back(c1);
}
int max=-1,cnt=1;
for(int i=1;i<=n;i++)
{
fill(vis,vis+n+1,0);
fill(layer,layer+n+1,0);
int tmp=0;
dfs(i,tmp);
bool flag=true;
for(int j=1;j<=n;j++)
{
if(vis[j]==false)
{
int t;
flag=false;
cnt++;
dfs(j,t);
}
}
if(!flag) break;
if(flag&&tmp>max)
{
ans.clear();
max=tmp;
ans.push_back(i);
}
else if(flag&&tmp==max)
ans.push_back(i);
}
if(ans.size()==0)
printf("Error: %d components",cnt);
else
for(int i=0;i<ans.size();i++)
printf("%d\n",ans[i]);
}
【例】*A1034 Head of a Gang (30 分)
ATTENTION
- DFS模板并不会遍历图中所有的边,但这道题需要遍历一个连通分支例的所有边求边权和,所以要在DFS模板上进行修改。
- 如果将DFS时所有结点的相邻边的边权全部相加,由于这是一个无向图,所以每条边都会被加两次,所以在每一个连通分支结束后,sum需要除以2。(晴神的做法是:在遍历到一条边g[u][v]后,将g[v][u]置0,即删除这条边,同时防止走回头路)
- 题目中给的n是边数,所以顶点数应该是<=2000。
- 由于map<type1,type2>自动根据type1进行递增排序,所以将type1设为head的名字,就实现了字典序排序。
- 点权可以在输入图时就计算得到!
- 对于每个连通分支需要计算的信息,如head、sum等,在dfs的同时完成!
#include <string>
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
const int maxn=2010;
const int inf=1e9+10;
int n,k,idx=0;
int g[maxn][maxn],weight[maxn]={0};
bool vis[maxn]={false};
map<string,int> nameToIdx;
map<int,string> idxToName;
map<string,int> headToCnt;
void dfs(int u,int &head,int &cnt,int &sum)
{
vis[u]=true;
for(int i=1;i<=idx;i++)
{
if(g[u][i]!=inf)
{
if(weight[u]>weight[head])
head=u;
sum+=g[u][i];
if(vis[i]==false)
{
cnt++;
dfs(i,head,cnt,sum);
}
}
}
}
void dfsTrave()
{
for(int i=1;i<=idx;i++)
{
int max=0,head=-1,cnt=0,sum=0;
if(vis[i]==false)
{
max=weight[i],head=i,cnt=1,sum=0;
dfs(i,head,cnt,sum);
//cout<<head<<" "<<cnt<<" "<<sum<<"\n";
}
if(cnt>2&&sum>2*k)
{
headToCnt[idxToName[head]]=cnt;
}
}
}
int main()
{
cin>>n>>k;
fill(g[0],g[0]+maxn*maxn,inf);
for(int i=0;i<n;i++)
{
string name1,name2;
int time;
cin>>name1>>name2>>time;
if(nameToIdx[name1]==0)
{
idx++;
nameToIdx[name1]=idx;
idxToName[idx]=name1;
}
if(nameToIdx[name2]==0)
{
idx++;
nameToIdx[name2]=idx;
idxToName[idx]=name2;
}
int u=nameToIdx[name1];
int v=nameToIdx[name2];
if(g[u][v]==inf||g[v][u]==inf)
{
g[u][v]=time;
g[v][u]=time;
}
else
{
g[u][v]+=time;
g[v][u]+=time;
}
weight[u]+=time;
weight[v]+=time; //点权可以在创建图的时候计算得到
}
dfsTrave();
cout<<headToCnt.size()<<"\n";
for(map<string,int>::iterator it=headToCnt.begin();it!=headToCnt.end();it++)
cout<<it->first<<" "<<it->second<<"\n";
return 0;
}
【例】A1154 Vertex Coloring (25 分)
labeling n. 标签;标记;[计] 标号
vertex n. 顶点
indices n. 指数;目录(index的复数)
ATTENTION
- 因为有多组数据,要注意清空。PAT很少考一个test里多组数据。其实在每个test里声明vector存储顶点颜色。
- 用邻接矩阵会超内存,顶点数<=1000采用邻接矩阵!!!
- 遍历所有顶点的邻接点,看颜色有没有重复,别忘了把该顶点的颜色也算进去(五分啊五分)。
- 细节细节细节!!!你很容易在细节上扣分,下笔谨慎些。
#include <cstdio>
#include <algorithm>
#include <set>
#include <vector>
using namespace std;
const int maxn=10010;
const int INF=1e9+10;
int n,m,k;
vector<int> g[maxn];
int color[maxn];
set<int> colorNum;
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d %d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
scanf("%d",&k);
for(int i=0;i<k;i++)
{
fill(color,color+maxn,0);
colorNum.clear();
for(int j=0;j<n;j++)
scanf("%d",&color[j]);
bool ans=true;
for(int u=0;u<n;u++)
{
bool flag=true;
colorNum.insert(color[u]); //忘记把第一个结点的颜色插入set了 orz
for(vector<int>::iterator it=g[u].begin();it!=g[u].end();it++)
{
if(color[u]==color[*it])
{
flag=false;
break;
}
else
colorNum.insert(color[*it]);
}
if(flag==false)
{
ans=false;
break;
}
}
if(ans)
printf("%d-coloring\n",colorNum.size());
else
printf("No\n");
}
return 0;
}
【例】A1134 Vertex Cover (25 分)
incident adj. [光] 入射的;附带的,附属的;伴随而来的
ATTENTION
- 第一思路是遍历整个图所有的边。所以把这道题放在图的遍历这里了。
- 柳神太强了。我怎么就没想到考虑点和边的对应关系呢orz 转换成散列哈希太强了TAT。换位思考,主客互换。
渣渣做法:
#include <cstdio>
#include <vector>
#include <map>
using namespace std;
int n,m,k,nv;
vector<int> g[10010];
map<int,int> mp;
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
int c1,c2;
scanf("%d %d",&c1,&c2);
g[c1].push_back(c2);
g[c2].push_back(c1);
}
scanf("%d",&k);
for(int i=0;i<k;i++)
{
mp.clear();
scanf("%d",&nv);
for(int j=0;j<nv;j++)
{
int tmp;
scanf("%d",&tmp);
mp[tmp]=1;
}
//judge
bool isYes=true;;
for(int j=0;j<n;j++)
{
if(mp[j]==1) continue;
bool flag=true;
for(int v=0;v<g[j].size();v++)
{
if(mp[g[j][v]]==0)
{
flag=false;
break;
}
}
if(!flag)
{
isYes=false;
printf("No\n");break;
}
}
if(isYes) printf("Yes\n");
}
}
大神版:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n,m,k,nv;
bool vis[10010]={0};
vector<int> v[10010];
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int c1,c2;
cin>>c1>>c2;
v[c1].push_back(i);
v[c2].push_back(i);
}
cin>>k;
while(k--)
{
fill(vis,vis+m,0);
cin>>nv;
int flag=0;
for(int i=0;i<nv;i++)
{
int u;
cin>>u;
for(int j=0;j<v[u].size();j++)
vis[v[u][j]]=true;
}
for(int i=0;i<m;i++)
{
if(vis[i]==false)
{
flag=-1;
break;
}
}
if(flag==0) cout<<"Yes\n";
else cout<<"No\n";
}
}
【例】A1076 Forwards on Weibo (30 分)
foward v.转发
follower n.跟随者;粉丝
ATTENTION
- 这道题的边是有向边,且
input
的方向与实际遍历的边的方向相反。 - 因为可能产生环,所以要确保每个结点只会被bfs到一次。
- 起始节点
layer=0
- 如果用结构体
struct node{
int id; //结点编号
int layer; //层数
};
#include <cstdio>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=1010;
int n,l,k,layer[maxn]={0};
vector<int> adj[maxn];
bool inq[maxn]={false};
int bfs(int u)
{
queue<int> q;
q.push(u);
inq[u]=true;
int cnt=0;
layer[u]=0;
while(!q.empty())
{
int front=q.front();
if(layer[front]>=l)
return cnt;
q.pop();
for(int i=0;i<adj[front].size();i++)
{
int v=adj[front][i];
if(inq[v]==false)
{
layer[v]=layer[front]+1;
cnt++;
q.push(v);
inq[v]=true;
}
}
}
}
int main()
{
scanf("%d %d",&n,&l);
for(int i=1;i<=n;i++)
{
int nv,v;
scanf("%d",&nv);
for(int j=0;j<nv;j++)
{
scanf("%d",&v);
adj[v].push_back(i);
}
}
scanf("%d",&k);
for(int i=0;i<k;i++)
{
fill(inq,inq+maxn,0);
fill(layer,layer+maxn,0);
int u;
scanf("%d",&u);
int ans=bfs(u);
printf("%d\n",ans);
}
}
【例】A1126 Eulerian Path (25 分)
circuit n.环,回路
ATTENTION
- 注意欧拉图和半欧拉图都要是连通的。
#include <cstdio>
int n,m;
int de[510]={0};
int g[510][510]={0};
bool vis[510]={false};
void dfs(int u)
{
vis[u]=true;
for(int v=1;v<=n;v++)
{
if(vis[v]==false&&g[u][v]==1)
dfs(v);
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
int c1,c2;
scanf("%d %d",&c1,&c2);
de[c1]++;
de[c2]++;
g[c1][c2]=g[c2][c1]=1;
}
int even=0,odd=0;
for(int i=1;i<=n;i++)
{
if(de[i]%2==0) even++;
else odd++;
if(i==1) printf("%d",de[i]);
else printf(" %d",de[i]);
}
bool flag=true;
dfs(1);
for(int i=1;i<=n;i++)
{
if(vis[i]==false)
{
flag=false;
break;
}
}
if(flag&&even==n) printf("\nEulerian");
else if(flag&&even==n-2&&odd==2) printf("\nSemi-Eulerian");
else printf("\nNon-Eulerian");
return 0;
}
10.4 最短路径
10.4.1 迪杰斯特拉算法
【例】A1003 Emergency
#include <cstdio>
#include <algorithm>
using namespace std; //使用algorithm需要这句话
const int MAXN=1000;
const int INF=1e9;
int n,m,s,t;
int G[MAXN][MAXN];
bool vis[MAXN]={false};
int d[MAXN]; //最短路
int rescueNum[MAXN];
int maxRes[MAXN]; //最大救援人数
int maxNum[MAXN];
void dij(int s)
{
//initial
fill(d,d+MAXN,INF);
d[s]=0;
fill(maxRes,maxRes+MAXN,0);
maxRes[s]=rescueNum[s];
fill(maxNum,maxNum+MAXN,0);
maxNum[s]=1; //最短路条数
//n次循环
for(int i=0;i<n;i++)
{
//寻找最近的那个
int u=-1,min=INF;
for(int j=0;j<n;j++)
{
if(vis[j]==false&&d[j]<min)
{
u=j;
min=d[u];
}
}
if(u==-1) return; //!!!!!!!!!!
//update
vis[u]=true;
for(int v=0;v<n;v++)
{
if(vis[v]==false&&G[u][v]!=INF)
{
if(d[v]>d[u]+G[u][v])
{
d[v]=d[u]+G[u][v];
maxRes[v]=maxRes[u]+rescueNum[v];
maxNum[v]=maxNum[u]; //这里很重要!!!!!!!!!
}
else if(d[v]==d[u]+G[u][v])
{
maxNum[v]+=maxNum[u]; //这里很重要!!!!!!!!!
if(maxRes[v]<maxRes[u]+rescueNum[v])
maxRes[v]=maxRes[u]+rescueNum[v];
}
}
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
fill(G[0],G[0]+MAXN*MAXN,INF); //图的初始化,很重要!!!
for(int i=0;i<n;i++)
{
scanf("%d",&rescueNum[i]);
}
for(int i=0;i<m;i++)
{
int c1,c2,l;
scanf("%d%d%d",&c1,&c2,&l);
G[c1][c2]=l;
G[c2][c1]=l;
}
dij(s);
printf("%d %d",maxNum[t],maxRes[t]);
}
【例】A1018 Public Bike Management (30 分)
ATTENTION
- 一开始竟然忘记 初始化图 了,你这个大憨憨!!!
- 注意选择的路径上所有的station都要变得perfect,所以要确保每个的
weight
都为0
。还要注意tmpPath
是倒序存储的,所以要倒着调整。
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=510;
const int inf=1e9+10;
int cmax,n,sp,m,g[maxn][maxn],weight[maxn];
int d[maxn];
bool vis[maxn];
vector<int> pre[maxn];
void dij(int s)
{
fill(d,d+maxn,inf);
fill(vis,vis+maxn,0);
d[s]=0;
for(int i=0;i<=n;i++)
{
int u=-1,min=inf;
for(int j=0;j<=n;j++)
{
if(vis[j]==false&&d[j]<min)
{
u=j;
min=d[j];
}
}
if(u==-1) return;
vis[u]=true;
for(int i=0;i<=n;i++)
{
if(vis[i]==false&&g[u][i]!=inf)
{
if(d[i]>d[u]+g[u][i])
{
pre[i].clear();
pre[i].push_back(u);
d[i]=d[u]+g[u][i];
}
else if(d[i]==d[u]+g[u][i])
{
pre[i].push_back(u);
}
}
}
}
}
vector<int> tmpPath,path;
int sendcnt=inf,backcnt=inf;
void findPath(int u)
{
if(u==0)
{
tmpPath.push_back(u);
// for(int i=0;i<tmpPath.size();i++)
// printf("%d",tmpPath[i]);
// printf("\n");
int tmps=0,tmpc=0;
for(int i=tmpPath.size()-2;i>=1;i--)
{
int v=tmpPath[i];
if(weight[v]>=0) tmpc+=weight[v];
else
{
if(tmpc+weight[v]<0)
{
tmps=0-tmpc-weight[v];
tmpc=0;
}
else
{
tmpc+=weight[v];
}
}
}
if(weight[sp]>=0) tmpc+=weight[sp];
else
{
if(tmpc+weight[sp]<0)
{
tmps+=0-tmpc-weight[sp];
tmpc=0;
}
else tmpc+=weight[sp];
}
if(tmps<sendcnt)
{
sendcnt=tmps;
path=tmpPath;
backcnt=tmpc;
}
else if(tmps==sendcnt)
{
if(backcnt>tmpc)
{
path=tmpPath;
backcnt=tmpc;
}
}
tmpPath.pop_back();
return ;
}
tmpPath.push_back(u);
for(int i=0;i<pre[u].size();i++)
{
findPath(pre[u][i]);
}
tmpPath.pop_back();
}
int main()
{
fill(g[0],g[0]+maxn*maxn,inf);
scanf("%d %d %d %d",&cmax,&n,&sp,&m);
for(int i=1;i<=n;i++) scanf("%d",&weight[i]);
for(int i=1;i<=n;i++)
weight[i]-=cmax/2;
weight[0]=0;
for(int i=0;i<m;i++)
{
int s1,s2,t;
scanf("%d %d %d",&s1,&s2,&t);
g[s1][s2]=g[s2][s1]=t;
}
dij(0);
findPath(sp);
printf("%d ",sendcnt);
for(int i=path.size()-1;i>=0;i--)
{
if(i==path.size()-1) printf("%d",path[i]);
else printf("->%d",path[i]);
}
printf(" %d",backcnt);
return 0;
}
【例】*A1030 Travel Plan
【例】*A1087 All Roads Lead to Rome (30 分)
client n.客户;顾客;委托人
ATTENTION
-
Dij+DFS太不熟练了啊啊啊啊啊!!!理解还不够!!!!!!
-
这道题有三个标尺。
第一标尺:cost最小
第二标尺:happiness最大
第三标尺:average happiness 最大
同时,还要输出最短路径的数量。 -
前两个标尺和最短路径的数量可以根据笔记直接求得。对于max average happiness,需要额外记录从起点到顶点u之间的顶点数目(记录在数组pt[]中)。
那么就是,如果以点u
为中介点,可以优化路径,那么pt[v]=pt[u]+1
。 -
因为顶点是三个字母表示的,一开始的想法是用字符串hash表示每个顶点,那么maxn=262626+10。但是没想到超内存了!!!事实上,可以把顶点的输入保存下来,用
map
结构与0~N-1
映射!!! -
思想不要那么僵化呀啊啊啊啊啊啊!!!
-
还有,自己在写的时候,输出路径极为不熟练!!!!!!
-
这道题真的写得心态爆炸哦。至今有两次写dij不了了之,这可能也是为什么我对dij有种畏惧。其中有一次应该是因为图没有初始化为不可达。这次也是,debug了好久发现有个for循环的范围搞错了。这也反映了自己对算法的不理解。还有待练习!
-
注意!!!输出路径的时候,要
return
啊啊啊啊啊!!! -
还有!
fill
函数和memset
函数!!!
#include <string.h>
memset(数组名,值,sizeof(数组名)); //按字节赋值,只赋值0或-1
#include <algorithm>
using namespace std;
fill(数组名,数组名+大小,值); //可以使任意值
//二维数组初始化
fill(g[0],g[0]+maxn*maxn,INF);
#include <iostream>
#include <string.h>
#include <string>
#include <map>
#include <algorithm> //fill
using namespace std;
const int maxn=210; //最大顶点数
const int INF=1e9; //不可达
int n,k,st; //st 起点
int g[maxn][maxn],weight[maxn]; //图,点权
//最短路径,最大点权,最短路径数目,目前路径上的顶点数,前驱结点
int d[maxn],w[maxn],num[maxn],pt[maxn],pre[maxn];
bool vis[maxn]={0};
map<string,int> cityToIdx; //城市名转换为idx
map<int,string> idxToCity; //idx转换为城市名
void dij(int s)
{
fill(d,d+maxn,INF);
memset(w,0,sizeof(w));
memset(num,0,sizeof(num));
memset(pt,0,sizeof(pt));
for(int i=0;i<n;i++)
pre[i]=i;
d[s]=0;
num[s]=1;
w[s]=weight[s];
for(int i=0;i<n;i++){
int u=-1,min=INF;
for(int j=0;j<n;j++){
if(vis[j]==false&&d[j]<min){
u=j;
min=d[j];
}
}
if(u==-1) return;
vis[u]=true;
for(int v=0;v<n;v++){
if(vis[v]==false&&g[u][v]!=INF){
if(d[v]>d[u]+g[u][v]){
d[v]=g[u][v]+d[u];
pre[v]=u;
pt[v]=pt[u]+1;
num[v]=num[u];
w[v]=w[u]+weight[v];
}
else if(d[v]==g[u][v]+d[u]){
num[v]+=num[u];
if(w[v]<w[u]+weight[v]){
pre[v]=u;
pt[v]=pt[u]+1;
w[v]=w[u]+weight[v];
}
else if(w[v]==w[u]+weight[v]){
double uAvg=(w[u]+weight[v])*1.0/(pt[u]+1);
double vAvg=(w[v])*1.0/(pt[v]);
if(uAvg>vAvg){
pt[v]=pt[u]+1;
pre[v]=u; //!!!
}
}
}
}
}
}
}
void printPath(int v)
{
if(v==0)
{
cout<<idxToCity[v];
return;
}
printPath(pre[v]);
cout<<"->"<<idxToCity[v];
}
int main()
{
string str;
cin>>n>>k>>str;
cityToIdx[str]=0;
idxToCity[0]=str;
for(int i=1;i<n;i++)
{
cin>>str;
cin>>weight[i];
cityToIdx[str]=i;
idxToCity[i]=str;
}
fill(g[0],g[0]+maxn*maxn,INF); //!!!!!!!!!
for(int i=0;i<k;i++)
{
string c1,c2;
int i1,i2;
cin>>c1>>c2;
i1=cityToIdx[c1];i2=cityToIdx[c2];
cin>>g[i1][i2];
g[i2][i1]=g[i1][i2];
//cout<<i1<<" "<<i2<<" "<<g[i1][i2]<<endl;
}
dij(0);
int end=cityToIdx["ROM"];
cout<<num[end]<<" "<<d[end]<<" "<<w[end]<<" "<<w[end]/pt[end]<<endl;
printPath(end);
return 0;
}
Dij+DFS 做法
ATTENTION
- 别忘了维护
string->int
和int->string
的两组映射! - 这里只记录了least cost和max happiness的最短路径,但是由于题目要求的最短路径的数量是least cost的,所以在dij中计算最短路径的数量!
tempPath
的pop_back()
还不是很懂,还要再理解一下!- 注意average happiness只要输出整数部分。
#include <iostream>
#include <string>
#include <string.h> //memset头文件
#include <vector>
#include <algorithm> //fill头文件
#include <map>
using namespace std;
const int maxn=210;
const int INF=1e9;
int n,k;
string st;
int g[maxn][maxn],d[maxn]; //图,cost
bool vis[maxn]={0};
int weight[maxn],w[maxn]; //点权
int num[maxn]={0};
map<string,int> cityToIdx;
map<int,string> idxToCity;
vector<int> pre[maxn]; //前驱结点
vector<int> tempPath,path;
double happyMax=0;
void dij(int s)
{
fill(d,d+maxn,INF);
memset(w,0,sizeof(w)); //memset用法
d[s]=0;
w[s]=0;
num[s]=1;
for(int i=0;i<n;i++)
{
int u=-1,min=INF;
for(int j=0;j<n;j++)
{
if(vis[j]==false&&d[j]<min)
{
u=j;
min=d[j];
}
}
if(u==-1) return;
vis[u]=true;
for(int v=0;v<n;v++)
{
if(vis[v]==false&&g[u][v]!=INF)
{
if(d[v]>d[u]+g[u][v])
{
d[v]=d[u]+g[u][v];
w[v]=w[u]+weight[v];
pre[v].clear();
pre[v].push_back(u);
num[v]=num[u];
}
else if(d[v]==d[u]+g[u][v])
{
pre[v].push_back(u);
num[v]+=num[u]; //!!!
if(w[v]<w[u]+weight[v])
{
w[v]=w[u]+weight[v];
pre[v].clear();
pre[v].push_back(u);
}
else if(w[v]==w[u]+weight[v])
{
pre[v].push_back(u);
}
}
}
}
}
}
void dfs(int t)
{
if(t==0) //到达起点,一条最短路径
{
tempPath.push_back(t);
int cnt=tempPath.size()-1;
double cur=w[tempPath[0]]*1.0/cnt;
if(cur>happyMax)
{
path=tempPath;
happyMax=cur;
}
tempPath.pop_back(); //!!!!!!!!!
}
tempPath.push_back(t);
for(int i=0;i<pre[t].size();i++)
{
dfs(pre[t][i]);
}
tempPath.pop_back();
}
int main()
{
cin>>n>>k>>st;
cityToIdx[st]=0;
idxToCity[0]=st;
for(int i=1;i<n;i++)
{
string str;int tmp;
cin>>str>>tmp;
cityToIdx[str]=i;
idxToCity[i]=str; //很重要!!!
weight[i]=tmp;
}
fill(g[0],g[0]+maxn*maxn,INF);
for(int i=0;i<k;i++)
{
string c1,c2;
cin>>c1>>c2;
int a=cityToIdx[c1],b=cityToIdx[c2];
cin>>g[a][b];
g[b][a]=g[a][b];
}
dij(0);
int end=cityToIdx["ROM"];
dfs(end);
cout<<num[end]<<" "<<d[end]<<" "<<w[end]<<" "<<(int)happyMax<<endl;
for(int i=path.size()-1;i>=0;i--)
{
string s=idxToCity[path[i]];
if(i!=0)
cout<<s<<"->";
else
cout<<s<<endl;
}
return 0;
}
【例】*A1072 Gas Station (30 分)
ATTENTION
- 这题好坑啊,样例一3.25精确到1位成了3.3,害得我以为要进位,然后最后一个点就死活过不去了呜呜呜TAT
- 看到accurate up to x decimal places,无脑用
%.md
即可!!!
#include <string>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1020;
const int INF=1e9;
int n,m,k,ds;
int g[maxn][maxn],d[maxn];
bool vis[maxn]={0};
int maxDis=0,idx=0;
double avgDis=0;
bool dij(int s)
{
fill(d,d+maxn,INF);
fill(vis,vis+maxn,0);
d[s]=0;
for(int i=1;i<=n+10;i++)
{
int u=0,min=INF;
for(int j=1;j<=10+n;j++)
{
if(vis[j]==false&&d[j]<min)
{
u=j;
min=d[j];
}
}
if(u==0) return false;
vis[u]=true;
for(int v=1;v<=n+10;v++)
{
if(vis[v]==false&&g[u][v]!=INF)
{
if(d[v]>d[u]+g[u][v])
d[v]=d[u]+g[u][v];
}
}
}
return true;
}
int strToIdx(string s)
{
int ans=0;
for(int i=0;i<s.size();i++)
{
ans=ans*10+s[i]-'0';
}
return ans+10;
}
int main()
{
cin>>n>>m>>k>>ds;
fill(g[0],g[0]+maxn*maxn,INF);
for(int i=1;i<=m+n;i++)
g[i][i]=0;
for(int i=0;i<k;i++)
{
string s1,s2;int h1,h2;
cin>>s1>>s2;
if(s1[0]!='G') h1=strToIdx(s1); //房子 11-n+10
else
{
if(s1.size()==3)
h1=10;
else
h1=s1[1]-'0'; //加油站 1-10
}
if(s2[0]!='G') h2=strToIdx(s2); //房子 11-n+10
else
{
if(s2.size()==3)
h2=10;
else
h2=s2[1]-'0'; //加油站 1-10
}
int w;
cin>>w;
if(w<g[h1][h2])
{
g[h1][h2]=w;
g[h2][h1]=w;
}
}
bool flag;
for(int i=1;i<=m;i++) //对于每一个候选车站,进行dfs
{
flag=true; //!!!
dij(i);
int tmp=INF;
double sum=0,avgSum=0;
for(int j=11;j<=10+n;j++)
{
sum+=d[j];
if(d[j]>ds) //不在服务范围内
{
flag=false;
break;
}
if(d[j]<tmp) //最小dis
tmp=d[j];
}
if(flag)
{
avgSum=sum*1.0/(double)n;
if(tmp>maxDis)
{
maxDis=tmp;
avgDis=avgSum;
idx=i;
}
else if(tmp==maxDis)
{
if(avgSum<avgDis)
{
avgDis=avgSum;
idx=i;
}
}
}
}
if(idx!=0)
{
cout<<"G"<<idx<<"\n";
printf("%.1f %.1f\n",(double)maxDis,avgDis);
}
else
{
cout<<"No Solution"<<endl;
}
return 0;
}
【例】A1111 Online Map (30 分)
intersection n. 交叉;十字路口;交集;交叉点
identical adj. 同一的;完全相同的
ATTENTION
- 这里有个有向边的问题。one-way如果为1,则这条路是单向的。
//intersection identical
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=510;
const int inf=1e9+10;
int len[maxn][maxn],time[maxn][maxn];
bool vis[maxn];
int d[maxn],t[maxn],n,m,pred[maxn],s,de;
vector<int> pre[maxn];
vector<int> shortest,fastest;
void dfsDis(int s)
{
fill(vis,vis+maxn,0); /
fill(d,d+maxn,inf);
fill(t,t+maxn,inf);
for(int i=0;i<n;i++) pred[i]=i;
d[s]=0;
t[s]=0;
for(int i=0;i<n;i++)
{
int u=-1,min=inf;
for(int j=0;j<n;j++)
{
if(vis[j]==false&&min>d[j])
{
u=j;
min=d[j];
}
}
if(u==-1) return;
vis[u]=true; /
for(int v=0;v<n;v++)
{
if(vis[v]==false)
{
if(d[v]>d[u]+len[u][v])
{
d[v]=d[u]+len[u][v];
pred[v]=u;
t[v]=t[u]+time[u][v];
}
else if(d[v]==d[u]+len[u][v]&&t[v]>t[u]+time[u][v])
{
pred[v]=u;
t[v]=t[u]+time[u][v];
}
}
}
}
}
void getPath(int tt)
{
if(tt==s)
{
shortest.push_back(tt);
return;
}
shortest.push_back(tt);
getPath(pred[tt]);
}
void dfsTime(int s)
{
fill(vis,vis+maxn,0); /
fill(t,t+maxn,inf);
t[s]=0;
for(int i=0;i<n;i++)
{
int u=-1,min=inf;
for(int j=0;j<n;j++)
{
if(vis[j]==false&&min>t[j])
{
u=j;
min=t[j];
}
}
if(u==-1) return;
vis[u]=true; /
for(int v=0;v<n;v++)
{
if(vis[v]==false)
{
if(t[v]>t[u]+time[u][v])
{
pre[v].clear();
pre[v].push_back(u);
t[v]=t[u]+time[u][v];
}
else if(t[v]==t[u]+time[u][v])
{
pre[v].push_back(u);
}
}
}
}
}
vector<int> tmp;
int minIn=inf;
void getFast(int tt)
{
if(tt==s)
{
tmp.push_back(tt);
if(tmp.size()<minIn)
{
minIn=tmp.size();
fastest=tmp;
}
tmp.pop_back();
return ;
}
tmp.push_back(tt);
for(int i=0;i<pre[tt].size();i++)
getFast(pre[tt][i]);
tmp.pop_back();
}
int main()
{
scanf("%d %d",&n,&m);
fill(len[0],len[0]+maxn*maxn,inf); /
fill(time[0],time[0]+maxn*maxn,inf);
for(int i=0;i<m;i++)
{
int v1,v2,one_way,length,cost;
scanf("%d %d %d %d %d",&v1,&v2,&one_way,&length,&cost);
len[v1][v2]=length;
time[v1][v2]=cost;
if(one_way==0)
{
len[v2][v1]=length;
time[v2][v1]=cost;
}
}
scanf("%d %d",&s,&de);
dfsDis(s);
getPath(de);
// for(int i=0;i<shortest.size();i++)
// printf("%d ",shortest[i]);
dfsTime(s);
getFast(de);
// for(int i=0;i<fastest.size();i++)
// printf("%d ",fastest[i]);
if(fastest==shortest)
{
printf("Distance = %d; Time = %d:",d[de],t[de]);
for(int i=fastest.size()-1;i>=0;i--)
{
printf(" %d",fastest[i]);
if(i!=0) printf(" ->");
}
}
else
{
printf("Distance = %d:",d[de]);
for(int i=shortest.size()-1;i>=0;i--)
{
printf(" %d",shortest[i]);
if(i!=0) printf(" ->");
}
printf("\nTime = %d:",t[de]);
for(int i=fastest.size()-1;i>=0;i--)
{
printf(" %d",fastest[i]);
if(i!=0) printf(" ->");
}
}
return 0;
}
10. 拓扑排序
【例】A1146 Topological Order (25 分)
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=1010;
int n,m,k;
vector<int> adj[maxn];
vector<int> ans;
bool vis[maxn]={false};
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
int c1,c2;
scanf("%d %d",&c2,&c1);
adj[c1].push_back(c2);
}
scanf("%d",&k);
for(int i=0;i<k;i++)
{
fill(vis,vis+n,0);
int u;
int cnt=0;
for(int j=1;j<=n;j++)
{
scanf("%d",&u);
for(int h=0;h<adj[u].size();h++)
{
if(vis[adj[u][h]]==false)
cnt++;
}
vis[u]=true;
}
if(cnt!=0)
ans.push_back(i);
}
for(int i=0;i<ans.size();i++)
{
if(i==0) printf("%d",ans[i]);
else printf(" %d",ans[i]);
}
return 0;
}