PAT-图与动规

参考资料:《算法笔记》、柳神csdn博客

图的遍历

A1013【有空试着用并查集做做看】

问题:题目给出一个无向图,并给出几个点,要求去掉这个点后还需要多少条边才可以将这个图变成连通图
思路:dfs判断有几个连通分量即可
关键:dfs;这道题也可以用并查集做。

DFS代码:

#include<cstdio>
using namespace std;
const int maxn = 1010;
int N,M,K,G[maxn][maxn],vis[maxn];
void dfs(int x,int m){
	vis[x] = 1;
	for(int i=1;i<=N;i++){
		if(i==m)
			continue;
		if(G[x][i]&&!vis[i])
			dfs(i,m);
	}
}
void dfsTrave(int m,int &num){
	for(int i=1;i<=N;i++){
		if(i==m)
			continue;
		if(!vis[i]){
			num++;
			dfs(i,m);
		}
	}
}
int main(){
	scanf("%d%d%d",&N,&M,&K);
	for(int i=0,m,n;i<M;i++){
		scanf("%d%d",&m,&n);
		G[m][n] = 1;
		G[n][m] = 1;
	}
	for(int i=0,m;i<K;i++){
		for(int j=1;j<=N;j++)
			vis[j]=0;
		scanf("%d",&m);
		int num = 0;
		dfsTrave(m,num);
		printf("%d\n",num-1);
	}
}

A1076

问题:给一个图,要求给定点的内k层的所有点的个数
思路:结构体+bfs
关键:

1.在bfs中,特别要注意对于当前这个点,vis应该在这个点加入队列后马上置1,不然访问到其他节点时又会重复加入这个点

2.要学会用结构体加上bfs来得到一个图的levelOrder(自创说法)

ac代码:

#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 1010;
int N,L;
struct node{
	int data;
	int level;
};
vector<node> a[maxn];
void bfs(int m){
	int vis[maxn]={0};
	queue<node> q;
	int num = 0;
	node nd;
	nd.data = m;
	nd.level = 0;
	q.push(nd);
	vis[nd.data] = 1;
	while(!q.empty()){
		node now = q.front();
		q.pop();
		for(auto d:a[now.data]){
			d.level = now.level + 1;
			if(!vis[d.data]&&d.level<=L){
				q.push(d);
				vis[d.data] = 1;
				num++;
			}
		}
	}
	printf("%d\n",num);
}
int main(){
	node user;
	scanf("%d %d",&N,&L);
	for(int i=1,m,n;i<=N;i++){
		scanf("%d",&n);
		user.data = i;
		for(int j=0;j<n;j++){
			scanf("%d",&m);
			a[m].push_back(user);
		}
	}
	int queryN;
	scanf("%d",&queryN);
	for(int i=0,m;i<queryN;i++){
		scanf("%d",&m);
		bfs(m);
	}
}

最短路径

A1003

问题:给定一个图,求出最短路径,如果有多条,则还要求出最短路径中点权值之和的最大值
思路:
1.以距离为第一尺度,路径上各个点的权值之和为第二尺度去求最短路径
2.Dijkstra + DFS 即先找出距离上最短的那么多条路径,然后在这些路径中再找关于第二尺度要求的路径。
关键:
1.对于二维数组用fill函数时要注意,数组头指针为G[0]而不是G !

int G[maxv][maxv];
fill(G[0],G[0]+maxv*maxv,INF);

2.无他,唯手熟尔
ac代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxv = 1000;
const int INF = 1e9;
int G[maxv][maxv],weight[maxv],d[maxv],num[maxv],w[maxv],vis[maxv];
int N,M,c1,c2;
void Dijkstra(int s){
	fill(d,d+maxv,INF);
	d[s] = 0;
	num[s] = 1;
	w[s] = weight[s];
	for(int i=0;i<N;i++){
		int MIN = INF,u=-1;
		for(int j=0;j<N;j++){
			if(!vis[j]&&d[j]<MIN){
				MIN = d[j];
				u = j;
			}
		}
		if(u==-1)return;
		vis[u] = 1;
		for(int v=0;v<N;v++){
			if(!vis[v]&&G[u][v]!=INF){
				if(d[u]+G[u][v]<d[v]){
					d[v] = d[u] + G[u][v];
					w[v] = w[u] + weight[v];
					num[v] = num[u];
				}else if(d[u]+G[u][v]==d[v]){
					if(w[u]+weight[v]>w[v]){
						w[v] = w[u]+weight[v];
					}
					num[v]+=num[u];
				}
			}
		}
	}
}
int main(){
	fill(G[0],G[0]+maxv*maxv,INF);
	scanf("%d%d%d%d",&N,&M,&c1,&c2);
	for(int i=0;i<N;i++)
		scanf("%d",&weight[i]);
	for(int i=0,m,n;i<M;i++){
		scanf("%d%d",&m,&n);
		scanf("%d",&G[m][n]);
		G[n][m] = G[m][n];
	}
	Dijkstra(c1);
	printf("%d %d",num[c2],w[c2]);
	return 0;
}
Dijkstra + DFS ac代码

虽然代码量可能会大一点,但胜在思路清晰。
下面的代码一次ac

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int INF = 1e9;
const int maxv = 1010;
int N,M,c1,c2,G[maxv][maxv],d[maxv],weight[maxv],vis[maxv];
vector<int> pre[maxv];
void Dijkstra(int s){
	fill(d,d+maxv,INF);
	d[s] = 0;
	for(int i=0;i<N;i++){
		int MIN=INF,u=-1;
		for(int j=0;j<N;j++){
			if(!vis[j]&&d[j]<MIN){
				MIN = d[j];
				u = j;
			}
		}
		if(u==-1)return;
		vis[u]=1;
		for(int v=0;v<N;v++){
			if(!vis[v]&&G[u][v]>0){
				if(d[u]+G[u][v]<d[v]){
					d[v] = d[u]+G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}else if(d[u]+G[u][v]==d[v])
					pre[v].push_back(u);
			}
		}
	}
}
vector<int> path;
vector<int> tempPath;
int optValue=-1,num=0;
void DFS(int v){
	if(v==c1){
		num++;
		tempPath.push_back(v);
		int value=0;
		for(int i=tempPath.size()-1;i>=0;i--){
			value+=weight[tempPath[i]];
		}
		if(value>optValue){
			optValue = value;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for(int i=0;i<pre[v].size();i++)
		DFS(pre[v][i]);
	tempPath.pop_back();
}
int main(){
	scanf("%d%d%d%d",&N,&M,&c1,&c2);
	fill(G[0],G[0]+maxv*maxv,INF);
	for(int i=0;i<N;i++)
		scanf("%d",&weight[i]);
	for(int i=0,m,n;i<M;i++){
		scanf("%d%d",&m,&n);
		scanf("%d",&G[m][n]);
		G[n][m] = G[m][n];
	}
	Dijkstra(c1);
	DFS(c2);
	printf("%d %d",num,optValue);
}

Bellman-Ford

这里在统计有多少条最短路径时,需要用一个set来记录所有的前驱,每次找到新的一条最短路径时都需要重新更新最短路径数num。因为BF算法一共vectex-1次循环,每次都遍历edge条边,因此很有可能会把一些边重复加入,因此需要用一个set去记录每个点的前驱,然后重新计算。例子如下:
在这里插入图片描述
说明:

如果按照Dijkstra中对num的做法,那么

第一轮,num[A]=num[B]=num[C]=1
第二轮,看到通过B也得到了一条C的最短路径,因此num[C] += num[B]后 num[C] = 2;然后又发现通过A可以得到一条B的最短路径,此时num[B] += num[A]后,num[B] = 2
第三轮,经过B到C,num[C] += num[B],num[C] = 4;而这显然是错误的 GG;

正常做法

每次得到一条最短路径时,将u加入set,然后在统计num,像上面第三轮中,pre[C] = {B,S}
pre[B] = {A,S};num[B] += num[A],num[B] += num[S]    ⟺    \iff num[B]=2 then num[C] += num[B],num[C]+=num[S]    ⟺    \iff num[C] = 3 (correct)

#include<cstdio>
#include<set>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1010;
const int INF = 1e9;
int N,M,C1,C2,d[maxn],weight[maxn],w[maxn],num[maxn];
set<int> pre[maxn];
struct node{
	int v,dis;
	node(int _v,int _dis):v(_v),dis(_dis){}
};
vector<node> adj[maxn];
void BF(int s){
	fill(d,d+maxn,INF);
	d[s] = 0;
	w[s] = weight[s];
	num[s] = 1;
	int v,dis;
	for(int i=0;i<N-1;i++){
		for(int u=0;u<N;u++){
			for(int j=0;j<adj[u].size();j++){
				v = adj[u][j].v;
				dis = adj[u][j].dis;
				if(d[u] + dis < d[v]){
					d[v] = d[u] + dis;
					w[v] = w[u] + weight[v];
					num[v] = num[u];
					pre[v].clear();
					pre[v].insert(u);
				}else if(d[u] + dis == d[v]){
					if(w[u] + weight[v] > w[v]){
						w[v] = w[u] + weight[v];
					}
					pre[v].insert(u);
					num[v] = 0;
					for(auto k:pre[v]){
						num[v] += num[k];
					}
				}
			}
		}
	}
}
int main(){
	scanf("%d%d%d%d",&N,&M,&C1,&C2);
	for(int i=0;i<N;i++){
		scanf("%d",&weight[i]);
	}
	for(int i=0,m,n,p;i<M;i++){
		scanf("%d%d%d",&m,&n,&p);
		adj[m].push_back(node(n,p));
		adj[n].push_back(node(m,p));
	}
	BF(C1);
	printf("%d %d",num[C2],w[C2]);
}

A1030

问题:与上面那题类似,只不过第二尺度是边权。
思路:直接Dijkstra或者Dijkstra+DFS
关键:
1.对于无向图,要注意对于边长或者边权重都要双向赋值 !
2.fill函数很好用 memset要慎用(头文件为cstring)

直接Dijkstra

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxv = 1010;
const int INF = 1e9;
int N,M,S,D,G[maxv][maxv],cost[maxv][maxv],d[maxv],w[maxv],vis[maxv],pre[maxv];
void Dijkstra(int s){
	fill(d,d+maxv,INF);
	d[s] = 0;
	w[s] = 0;
	for(int i=0;i<N;i++)pre[i]=i;
	for(int i=0;i<N;i++){
		int MIN = INF,u=-1;
		for(int j=0;j<N;j++){
			if(!vis[j]&&MIN>d[j]){
				MIN = d[j];
				u = j;
			}
		}
		if(u==-1)return;
		vis[u]=1;
		for(int v=0;v<N;v++){
			if(!vis[v]&&G[u][v]!=INF){
				if(d[u]+G[u][v]<d[v]){
					d[v] = d[u]+G[u][v];
					w[v] = cost[u][v] + w[u];
					pre[v]=u;
				}else if(d[u]+G[u][v]==d[v]&&w[u]+cost[u][v]<w[v]){
					w[v] = w[u] + cost[u][v];
					pre[v]=u;
				}
			}
		}
	}
}
vector<int> path;
void DFS(int v){
	if(v==S){
		path.push_back(v);
		return;
	}
	DFS(pre[v]);
	path.push_back(v);
}
int main(){
	fill(G[0],G[0]+maxv*maxv,INF);
	scanf("%d%d%d%d",&N,&M,&S,&D);
	for(int i=0,m,n;i<M;i++){
		scanf("%d%d",&m,&n);
		scanf("%d",&G[m][n]);
		G[n][m] = G[m][n];
		scanf("%d",&cost[m][n]);
        cost[n][m] = cost[m][n];
	}
	Dijkstra(S);
	DFS(D);
	for(int i=0;i<path.size();i++)
		printf("%d ",path[i]);
	printf("%d %d",d[D],w[D]);
	return 0;
}

Dijkstra+DFS

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxv = 1010;
const int INF = 1e9;
int N,M,S,D,G[maxv][maxv],vis[maxv],d[maxv],cost[maxv][maxv];
vector<int> pre[maxv];
void Dijkstra(int s){
	fill(d,d+maxv,INF);
	d[s] = 0;
	for(int i=0;i<N;i++){
		int MIN = INF,u=-1;
		for(int j=0;j<N;j++){
			if(!vis[j]&&MIN>d[j]){
				MIN = d[j];
				u = j;
			}
		}
		if(u==-1)return;
		vis[u]=1;
		for(int v=0;v<N;v++){
			if(!vis[v]&&G[u][v]!=INF){
				if(d[u]+G[u][v]<d[v]){
					d[v]=d[u]+G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}else if(d[u]+G[u][v]==d[v]){
					pre[v].push_back(u);
				}
			}
		}
	}
}
vector<int> path;
vector<int> tempPath;
int optValue=INF;
void DFS(int v){
	if(v==S){
		tempPath.push_back(v);
		int value=0;
		for(int i=tempPath.size()-1;i>0;i--){
			value+=cost[tempPath[i]][tempPath[i-1]];
		}
		if(optValue>value){
			optValue = value;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for(auto i:pre[v]){
		DFS(i);
	}
	tempPath.pop_back();
}
int main(){
	fill(G[0],G[0]+maxv*maxv,INF);
	scanf("%d%d%d%d",&N,&M,&S,&D);
	for(int i=0,m,n;i<M;i++){
		scanf("%d%d",&m,&n);
		scanf("%d",&G[m][n]);
		G[n][m] = G[m][n];
		scanf("%d",&cost[m][n]);
		cost[n][m] = cost[m][n];
	}
	Dijkstra(S);
	DFS(D);
	for(int i=path.size()-1;i>=0;i--)
		printf("%d ",path[i]);
	printf("%d %d",d[D],optValue);
}

A1018【review | 对题意的理解!】

问题:PBMC
思路:Dijkstra + DFS
关键:
1.题目中的sent from和take back这两个词要好好理解:
sent from 是从PBMC带过去的单车数量;
take back 是从问题站点带回来的单车数量。

我一开始的错误理解

认为只需要将一条路径上的所有单车数量加起来然后减去站点数 × C m a x \times C_{max} ×Cmax 即可。这样的话sent from和take back中必然有一个是0.这样实现起来可能会稍微简单一些,但是这样的想法是错误的。

看了他人题解后的正确思路

sent from应该是一路走过去各个站点所需要补充的单车数再减去途中能够take的单车数,所以最后sent from的部分是会有剩余的,而这部分就是take back 的数量。

因为你在后面站点多出来的哪些单车是不能够补充到前面的站点去的 !ok

写代码时可以想象自己沿着路往下走,Back代表手上还有那么多车,Need代表计划还需要那么多车,当手上没车时,需要多少就用Need加;当手上有车时,可以为沿路的站点进行补充。

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxv = 1010;
const int INF = 0x3fffffff;
int Cmax,N,Sp,M,c[maxv],G[maxv][maxv],d[maxv],vis[maxv];
vector<int> pre[maxv];
void Dijkstra(int s){
	fill(d,d+maxv,INF);
	d[s] = 0;
	for(int i=0;i<=N;i++){
		int MIN = INF,u=-1;
		for(int j=0;j<=N;j++){
			if(!vis[j]&&MIN>d[j]){
				MIN = d[j];
				u = j;
			}
		}
		if(u==-1)return;
		vis[u] = 1;
		for(int v=0;v<=N;v++){
			if(!vis[v]&&G[u][v]!=INF){
				if(d[u]+G[u][v]<d[v]){
					d[v] = d[u] + G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}else if(d[u]+G[u][v]==d[v]){
					pre[v].push_back(u);
				}
			}
		}
	}
}
int minNeed = INF;
int minBack = INF; 
vector<int> path;
vector<int> tempPath;
void DFS(int t){
	if(t==0){
		tempPath.push_back(0);
		int Need=0,Back=0;
		for(int i=tempPath.size()-2;i>=0;i--){
			//这里的tempPath.size()-1 是0 ! 
			int num_bike = c[tempPath[i]];
			if(num_bike<0){
				if(Back==0)
					Need += -num_bike;
				else{
					if(Back > -num_bike){
						Back += num_bike;
					}else{
						Need += -num_bike - Back;
						Back = 0;
					}
				}
			}else if(num_bike>0){
				Back += num_bike;
			}
		}
		//这里的判断是这道题的关键,看你有没有把题目读全读懂,/kk
		if(minNeed > Need){
			minNeed = Need;
			minBack = Back;
			path = tempPath;
		}else if(minNeed==Need&&minBack>Back){
			minBack = Back;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(t);
	for(auto k:pre[t]){
		DFS(k);
	}
	tempPath.pop_back();
}
int main(){
	fill(G[0],G[0]+maxv*maxv,INF);
	scanf("%d%d%d%d",&Cmax,&N,&Sp,&M);
	for(int i=1;i<=N;i++){
		scanf("%d",&c[i]);
		c[i]-=Cmax/2;
	}
	for(int i=0,m,n;i<M;i++){
		scanf("%d%d",&m,&n);
		scanf("%d",&G[m][n]);
		G[n][m] = G[m][n];
	}
	Dijkstra(0);
	DFS(Sp);
	printf("%d ",minNeed);
	for(int i=path.size()-1;i>=0;i--){
		if(i!=path.size()-1)
			printf("->");
		printf("%d",path[i]);
	}
	printf(" %d",minBack);
	return 0;
}

A1072【complex | review】

问题:从m个加油站里面选取1个站点,让他离居民区的最近的人最远,并且没有超出服务范围ds之内。如果有很多个最远的加油站,输出距离所有居民区距离平均值最小的那个。如果平均值还是一样,就输出按照顺序排列加油站编号最小的那个
思路:用Dijkstra计算出每个站点到居民区的最短距离,然后做判断。第一尺度为最短距离最大的优先,第二尺度为平均距离最小的优先。
关键:
1.首先就是对‘’Gxx‘’如何处理,这里可以用char[] 也可以用string
2.既然题目说如果最后都相等时则输出下标最小的,那么整体下标从小到大遍历一遍,对于相等的情况不做处理,那么最后得到的就一定是相同条件下下标最小的那个了
2.string.substr(pos,len) 取子串 很好用
3.用floyd去做的话,最后一个测试点会超时。

#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
const int maxn = 1020;
const int INF = 0x3fffffff;
int N,M,K,Ds,G[maxn][maxn],d[maxn],vis[maxn];
void Dijkstra(int s){
	fill(d,d+maxn,INF);
	fill(vis,vis+maxn,0);
	d[s] = 0;
	for(int i=1;i<=M+N;i++){
		int MIN = INF,u=-1;
		for(int j=1;j<=M+N;j++){
			if(!vis[j]&&MIN>d[j]){
				u = j;
				MIN = d[j];
			}
		}
		vis[u] = 1;
		for(int v=1;v<=M+N;v++){
			if(!vis[v]&&d[u]+G[u][v]<d[v]){
				d[v] = d[u]+G[u][v];
			}
		}
	}
}
int main(){
	freopen("1.txt","r",stdin);
	fill(G[0],G[0]+maxn*maxn,INF);
	scanf("%d%d%d%d",&N,&M,&K,&Ds);
	string p,q;
	int m;
	for(int i=0,x,y;i<K;i++){
		cin>>p>>q>>m;
		if(p[0]=='G'){
			p = p.substr(1);
			x = stoi(p)+N;
		}else{
			x = stoi(p);
		}
		if(q[0]=='G'){
			q = q.substr(1);
			y = stoi(q)+N;
		}else{
			y = stoi(q);
		}
		G[y][x] = G[x][y] = m;
	}
	int optIndex;
	int optL = -1;
	double optAvg;
	bool have = false;
	for(int i=N+1;i<=N+M;i++){
		Dijkstra(i);
		bool flag = true;
		int MIN=INF,sum=0;
		for(int j=1;j<=N;j++){
			if(d[j]>Ds){
				flag = false;
				break;
			}
			if(MIN>d[j])
				MIN = d[j];
			sum += d[j];
		}
		if(!flag) continue;
		have = true;
		if(optL<MIN){
			optL = MIN;
			optIndex = i;
			optAvg = (double)sum/N;
		}else if(optL==MIN&&optAvg*N>sum){
			optAvg = (double)sum/N;
			optIndex = i;
		}
	}
	if(have){
		printf("G%d\n",optIndex-N);
		printf("%.1f %.1f",(double)optL,optAvg);
	}else{
		printf("No Solution");
	}
	return 0;
}
优先队列优化的Dijkstra

vis其实可有可无,有了可以加快一点速度

#include<iostream>
#include<algorithm>
#include<set>
#include<queue>
using namespace std;
const int maxn = 1020;
const int INF = 0x3fffffff;
int N,M,K,Ds,G[maxn][maxn],d[maxn],vis[maxn];
struct node{
	int id;
	int d;
	node(int a,int b):id(a),d(b){}
	bool operator < (const node x) const{
		if(d==x.d)return id>x.id;
		return d>x.d;
	}
};
void Dijkstra(int s){
	fill(d,d+maxn,INF);
	fill(vis,vis+maxn,0);
	d[s] = 0;
	priority_queue<node> q;
	q.push(node(s,0));
	while(!q.empty()){
		node now = q.top();
		q.pop();
		int u = now.id;
		for(int v=1;v<=M+N;v++){
			if(d[u]+G[u][v]<d[v]){
				d[v] = d[u] + G[u][v];
				q.push(node(v,d[v]));
			}
		}
	}
}
int main(){
	freopen("1.txt","r",stdin);
	fill(G[0],G[0]+maxn*maxn,INF);
	scanf("%d%d%d%d",&N,&M,&K,&Ds);
	string p,q;
	int m;
	for(int i=0,x,y;i<K;i++){
		cin>>p>>q>>m;
		if(p[0]=='G'){
			p = p.substr(1);
			x = stoi(p)+N;
		}else{
			x = stoi(p);
		}
		if(q[0]=='G'){
			q = q.substr(1);
			y = stoi(q)+N;
		}else{
			y = stoi(q);
		}
		G[y][x] = G[x][y] = m;
	}
	int optIndex;
	int optL = -1;
	double optAvg;
	bool have = false;
	for(int i=N+1;i<=N+M;i++){
		Dijkstra(i);
		bool flag = true;
		int MIN=INF,sum=0;
		for(int j=1;j<=N;j++){
			if(d[j]>Ds){
				flag = false;
				break;
			}
			if(MIN>d[j])
				MIN = d[j];
			sum += d[j];
		}
		if(!flag) continue;
		have = true;
		if(optL<MIN){
			optL = MIN;
			optIndex = i;
			optAvg = (double)sum/N;
		}else if(optL==MIN&&optAvg*N>sum){
			optAvg = (double)sum/N;
			optIndex = i;
		}
	}
	if(have){
		printf("G%d\n",optIndex-N);
		printf("%.1f %.1f",(double)optL,optAvg);
	}else{
		printf("No Solution");
	}
	return 0;
}

A1087

问题:找路径,第一尺度为路径的总费用更少为更优,第二尺度是路径的总happiness值更高为更优,第三尺度是路径的平均happiness值更高为更优。
思路:Dijkstra + DFS
关键:
1.string与index的转换,可以用map写两个函数getIndex和getName。要注意,由于map=0用来判断这个点还没编号,因此我们有效的编号是从1开始的。当然,也可以不用函数,直接用map,在读入时一个个赋上index,这样就可以从0开始了。
2.观察题目可以发现,起始点是没有happiness值的,因此在计算平均的时候应该要减一 !

一把ac代码:

#include<iostream>
#include<map>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 210;
const int INF = 0x3fffffff;
int N,K,happiness[maxn],G[maxn][maxn],d[maxn],vis[maxn];
string s,t,st,ed="ROM";
map<string,int> stringToint;
map<int,string> intTostring;
int index = 1;
int getIndex(string str){
	if(stringToint[str]==0){
		stringToint[str] = index;
		intTostring[index] = str;
		return index++;
	}
	return stringToint[str];
}
string getName(int k){
	return intTostring[k];
}
vector<int> pre[maxn];
void Dijkstra(int s){
	fill(d,d+maxn,INF);
	d[s] = 0;
	for(int i=1;i<=N;i++){
		int MIN = INF,u=-1;
		for(int j=1;j<=N;j++){
			if(!vis[j]&&MIN>d[j]){
				MIN = d[j];
				u = j;
			}
		}
		if(u==-1)return;
		vis[u] = 1;
		for(int v=1;v<=N;v++){
			if(!vis[v]){
				if(d[u]+G[u][v]<d[v]){
					d[v] = d[u]+G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if(d[u]+G[u][v]==d[v]){
					pre[v].push_back(u);
				}
			}
		}
	}
}
vector<int> path;
vector<int> tempPath;
int pathNum = 0;
int optValue = -1;
double optAvg = -1.0;
void DFS(int v){
	if(v==getIndex(st)){
		pathNum++;
		tempPath.push_back(v);
		int value = 0;
		for(int i=tempPath.size()-1;i>=0;i--)
			value += happiness[tempPath[i]];
		int avg = value*1.0/(tempPath.size()-1);
		if(value>optValue){
			optValue=value;
			optAvg = avg;
			path = tempPath;
		}else if(value==optValue&&avg>optAvg){
			optAvg = avg;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for(auto k:pre[v]){
		DFS(k);
	}
	tempPath.pop_back();
}
int main(){
	fill(G[0],G[0]+maxn*maxn,INF);
	cin >> N >> K >> st;
	int p;
	for(int i=0;i<N-1;i++){
		cin >> s >> p;
		happiness[getIndex(s)] = p;
	}
	for(int i=0;i<K;i++){
		cin >> s >> t >> p;
		G[getIndex(s)][getIndex(t)] = p;
		G[getIndex(t)][getIndex(s)] = p;
	}
	Dijkstra(getIndex(st));
	DFS(getIndex(ed));
	printf("%d %d %d %d\n",pathNum,d[getIndex(ed)],optValue,(int)optAvg);
	for(int i=path.size()-1;i>=0;i--){
		cout << getName(path[i]);
		if(i!=0)
			printf("->");
	}
	return 0;
}

A1111

单身狗专属题^^
问题:
求两种路径。
第一种为长度最短路径,第一尺度为长度短者优,第二尺度为耗时短者优
第二种为耗时最短路径,第一尺度为耗时短者优,第二尺度为路径上结点总数少者优
思路:twofold Dijkstra +DFS
关键:
1.双倍的Dijkstra+DFS 思路比较清晰
2.注意细节,Time和Length别搞混。在Dijkstra中长度最短路径就是求长度,耗时最短路径就是求时间。而在DFS中可以说是反过来了,在DFS中长度最短路径求时间,耗时最短路径求路径上的结点数(path.size() )

ac代码:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 510;
const int INF = 0x3fffffff;
int N,M,st,ed,length[maxn][maxn],time[maxn][maxn],vis[maxn];
vector<int> pre[maxn];
void Dijkstra(int s,int w[maxn][maxn],int d[maxn]){
	for(int i=0;i<maxn;i++)pre[i].clear();
	fill(d,d+maxn,INF);
	fill(vis,vis+maxn,0);
	d[s] = 0;
	for(int i=0;i<N;i++){
		int MIN = INF,u=-1;
		for(int j=0;j<N;j++){
			if(!vis[j]&&MIN>d[j]){
				u = j;
				MIN = d[j];
			}
		}
		if(u==-1)return;
		vis[u] = 1;
		for(int v=0;v<N;v++){
			if(!vis[v]){
				if(d[u]+w[u][v]<d[v]){
					d[v] = d[u] + w[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}else if(d[u]+w[u][v]==d[v]){
					pre[v].push_back(u);
				}
			}
		}
	}
}
vector<int> pathLength;
vector<int> pathTime;
vector<int> tempPath;
int optTime = INF;
int optNum = INF;
void DFSLength(int v){
	if(v==st){
		tempPath.push_back(v);
		int value = 0;
		for(int i=tempPath.size()-1;i>0;i--){
			//要考虑单向,所以是 i -> i-1
			value += time[tempPath[i]][tempPath[i-1]];
		}
		if(value<optTime){
			optTime = value;
			pathLength = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for(auto k:pre[v]){
		DFSLength(k);
	}
	tempPath.pop_back();
}
void DFSTime(int v){
	if(v==st){
		tempPath.push_back(v);
		int value = tempPath.size();
		if(value<optNum){
			optNum = value;
			pathTime = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for(auto k:pre[v]){
		DFSTime(k);
	}
	tempPath.pop_back();
}
int main(){
	scanf("%d%d",&N,&M);
	fill(length[0],length[0]+maxn*maxn,INF);
	fill(time[0],time[0]+maxn*maxn,INF);
	for(int i=0,m,n,l,p,q;i<M;i++){
		scanf("%d%d%d%d%d",&m,&n,&l,&p,&q);
		if(l==1){
			length[m][n] = p;
			time[m][n] = q;
		}else{
			length[m][n] = length[n][m] = p;
			time[m][n] = time[n][m] = q;
		}
	}
	scanf("%d%d",&st,&ed);
	int dLength[maxn];
	int dTime[maxn];
	Dijkstra(st,length,dLength);
	DFSLength(ed);
	Dijkstra(st,time,dTime);
	DFSTime(ed);
	if(pathTime==pathLength){
		printf("Distance = %d; Time = %d: ",dLength[ed],dTime[ed]);
		for(int i=pathTime.size()-1;i>=0;i--){
			printf("%d",pathTime[i]);
			if(i!=0)
				printf(" -> ");
		}
	}else{
		printf("Distance = %d: ",dLength[ed]);
		for(int i=pathLength.size()-1;i>=0;i--){
			printf("%d",pathLength[i]);
			if(i!=0)
				printf(" -> ");
		}
		printf("\n");
		printf("Time = %d: ",dTime[ed]);
		for(int i=pathTime.size()-1;i>=0;i--){
			printf("%d",pathTime[i]);
			if(i!=0)
				printf(" -> ");
		}
	}
}

A1091

问题:给定一个三维数组,0表示正常1表示有肿瘤,肿瘤块的大小大于等于t才算作是肿瘤,让计算所有满足肿瘤块的大小
思路:三维空间的BFS
关键:
1.对在三维空间中对于一个点的邻接点进行遍历时,我的错误代码如下:

for(int t=-1;t<=1&&t!=0;t++){
	......
}

显然这里在t=0时就退出了 !

正确写法应该如下:

for(int t=-1;t<=1;t++){
	if(t==0)
		continue;
	......
}

ac代码:

#include<cstdio>
#include<queue>
using namespace std;
const int maxx = 1300;
const int maxy = 130;
const int maxz = 70;
int M,N,L,T,G[maxx][maxy][maxz],vis[maxx][maxy][maxz],ans=0;
struct node{
	int x,y,z;
	node(int a,int b,int c):x(a),y(b),z(c){}
};
bool judge(int x,int y,int z){
	if(x<0||x>M-1)
		return false;
	if(y<0||y>N-1)
		return false;
	if(z<0||z>L-1)
		return false;
	if(G[x][y][z]==0||vis[x][y][z])
		return false;
	return true;
}
void BFS(){
	queue<node> q;
	for(int i=0;i<M;i++){
		for(int j=0;j<N;j++){
			for(int k=0;k<L;k++){
				if(G[i][j][k]!=0&&!vis[i][j][k]){
					int num = 0;
					q.push(node(i,j,k));
					vis[i][j][k] = 1;
					while(!q.empty()){
						node now = q.front();
						int x = now.x;
						int y = now.y;
						int z = now.z;
						q.pop();num++;
						for(int t=-1;t<=1;t++){
							if(t==0)
								continue;
							if(judge(x+t,y,z)){
								q.push(node(x+t,y,z));
								vis[x+t][y][z] = 1;
							}
							if(judge(x,y+t,z)){
								q.push(node(x,y+t,z));
								vis[x][y+t][z] = 1;
							}
							if(judge(x,y,z+t)){
								q.push(node(x,y,z+t));
								vis[x][y][z+t] = 1;
							}
						}
					}
					if(num>=T){
						ans+=num;
					}
				}
			}
		}
	}
}
int main(){
	scanf("%d%d%d%d",&M,&N,&L,&T);
	for(int k=0;k<L;k++)
		for(int i=0;i<M;i++)
			for(int j=0;j<N;j++){
				scanf("%d",&G[i][j][k]);
			}
	BFS();
	printf("%d",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值