甲级1003 Emergency (25)(25 分)
L2-001 紧急救援 (25 分)
【题意】两道题题意是一样的,记录最短路径的数目和最多能够gather的人数。但是L2-001要输出路径;所以用dijkstra+dfs来做
【代码】注释掉是甲级的代码,没注释掉的话就是L2的了
#include<bits/stdc++.h>
using namespace std;
const int maxn=510;
const int inf=0x3f3f3f3f;
int e[maxn][maxn];
int dis[maxn];
int book[maxn];
int w[maxn];
vector<int>path[maxn];
vector<int>t,tt;
int n,m,st,ed;
int sum,maxnum;
void dijkstra(int x)
{
memset(dis,inf,sizeof(dis));
memset(book,0,sizeof(book));
dis[x]=0;
for(int i=0;i<n;++i)
{
int now=-1,minn=inf;
for(int j=0;j<n;++j)
if(!book[j] && minn>dis[j])
minn=dis[j],now=j;
if(now==-1)break;
book[now]=1;
for(int j=0;j<n;++j)
{
if(!book[j])
{
if(dis[j]>dis[now]+e[now][j])
{
dis[j]=dis[now]+e[now][j];
path[j].clear();
path[j].push_back(now);
}
else if(dis[j]==dis[now]+e[now][j])
path[j].push_back(now);
}
}
}
}
void dfs(int x)
{
if(x==st)
{
sum++;
t.push_back(x);
int mans=0;
for(int i=t.size()-1;i>=0;--i)
mans+=w[t[i]];
if(maxnum<mans)maxnum=mans,tt=t;
t.pop_back();
return;
}
t.push_back(x);
for(int i=0;i<path[x].size();++i)
dfs(path[x][i]);
t.pop_back();
}
int main()
{
scanf("%d%d%d%d",&n,&m,&st,&ed);
for(int i=0;i<n;++i)scanf("%d",&w[i]);
memset(e,inf,sizeof(e));
while(m--)
{
int a,b,d;scanf("%d%d%d",&a,&b,&d);
e[a][b]=e[b][a]=d;
}
sum=0;maxnum=0;
dijkstra(st);
dfs(ed);
printf("%d %d\n",sum,maxnum);
for(int i=tt.size()-1;i>=0;--i)
(i==0)?printf("%d\n",tt[i]):printf("%d ",tt[i]);
}
【注意】一般地,如果在回溯法中修改了辅助的全局变量,则一定要及时把它们恢复原状,除非要故意保留你的修改。
for(int i=0;i<n;i++)
{
if(vis[i]||dis[city][i]==-1)
continue;
vis[i]=1;//修改
dfs(i,length+dis[city][i],mans+man[i]);
vis[i]=0;//这里恢复原状
}
【DFS代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=505;
int n,m,c1,c2,num=1,MM=0;
int vis[maxn];
int dis[maxn][maxn];
int man[maxn];
int minn=0x3f3f3f3f;
void dfs(int city,int length,int mans)
{
if(city==c2)
{
if(length>minn)return;
if(length==minn){
num++; //数目增加
MM=max(mans,MM);//取最大的那个
}
else{
num=1; //数目归1
MM=mans;
minn=length;
}
// cout<<MM<<endl;
}
if(length>minn)return;
for(int i=0;i<n;i++)
{
if(vis[i]||dis[city][i]==-1)
continue;
vis[i]=1;
dfs(i,length+dis[city][i],mans+man[i]);
vis[i]=0;
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&c1,&c2);
memset(man,0,sizeof(man));
memset(dis,-1,sizeof(dis));
for(int i=0;i<n;i++)
scanf("%d",&man[i]);
for(int i=0;i<m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
dis[x][y]=dis[y][x]=z;
}
memset(vis,0,sizeof(vis));
dfs(c1,0,man[c1]);
cout<<num<<" "<<MM<<endl;
}
【BFS代码】emmm大佬的代码,,要用优先队列,并且要标记。如果只求最短路径而不统计数目的话其实就不用标记也不用用到优先队列。但是这里是求数目,如果不标记的话同一个点重复走,这个点所连接的点也重复记录了,这时,数目就会不对。还有就是注意,
if ( vis[to] == (vis[x] + dis[x][to]) )
{
sum[to] += sum[x];
people[to] = max(people[to],people[x] + man[to]);
q.push(to);
}
这里不是增加1,而是增加sum[x]。
#include<bits/stdc++.h>
using namespace std;
const int maxn=505;
int n,m,c1,c2,num=1,MM=0;
int vis[maxn];
int dis[maxn][maxn];
int man[maxn];
vector<int>f[maxn];
int sum[maxn],people[maxn];
int minn=0x3f3f3f3f;
struct cmp{
bool operator()(int a, int b){
return vis[a] > vis[b];
}
};
int vvis[maxn];
void bfs(int city)
{
memset(people,0,sizeof(people));
memset(sum,0,sizeof(sum));
memset(vis,0x3f3f3f3f,sizeof(vis));
memset(vvis,0,sizeof(vvis));
priority_queue<int,vector<int>,cmp>q;
q.push(city);
vis[city] = 0;
people[city] = man[city];
sum[city] = 1;
while(!q.empty())
{
int x=q.top();q.pop();
if (vvis[x]) continue;
vvis[x] = 1;
if (x == c2) continue;
int len = f[x].size();
for (int i = 0 ; i < len ; ++i)
{
int to = f[x][i];
if ( vis[to] == (vis[x] + dis[x][to]) )
{
sum[to] += sum[x];
people[to] = max(people[to],people[x] + man[to]);
q.push(to);
}
else
if ( vis[to] > (vis[x] + dis[x][to]) )
{
vis[to] = vis[x] + dis[x][to];
sum[to] = sum[x];
people[to] = people[x] + man[to];
q.push(to);
}
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&c1,&c2);
for(int i=0;i<n;i++){
scanf("%d",&man[i]);
f[i].clear();
}
for(int i=0;i<m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
dis[x][y]=dis[y][x]=z;
f[x].push_back(y);
f[y].push_back(x);
}
bfs(c1);
cout<<sum[c2]<<" "<<people[c2]<<endl;
}