0330南京理工机试模拟(五)

题目列表:在这里插入图片描述

第四题

P1907设计道路

考点:单源最短路径

分析:由题目已知可以计算任意两点之间的边权,求起点到终点的边权和可以达到的最小值,即暴力建图,两两相连,区分Dirt road和Rome road,然后求起点到终点的最短路
每条道路的长度=直线距离×权值;

方法一
思想: DFS+剪枝

/*--------4-------*/
//100.0 2.0
//2
//1.0 0.0
//2.0 1.0
//1 2
//0 0
//0.0 0.0
//2.0 2.0
#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
const int INF=10000000;
double ncD,ncR;
int n;
double x[maxn],y[maxn],G[maxn][maxn];
double minTot=10000000;
bool visit[maxn]={false};
void DFS(int s,double tot){
	if(s==n+1){
		if(tot<minTot) {
			minTot=tot;
			return ;
		}
	}else{
		for(int i=0;i<=n+1;i++){
			if(visit[i]==false){
				if(tot+G[s][i]<minTot){
    				visit[i]=true;
    				DFS(i,tot+G[s][i]);
    				visit[i]=false;
				}
			} 
	}
}
}
double dist(int i,int j){
	//下标为i,j的两点 
	return  sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
int main(){
// 	freopen("in.txt","r",stdin);
// 	freopen("out.txt","w",stdout);
	fill(G[0],G[0]+maxn*maxn,INF);
	scanf("%lf%lf",&ncD,&ncR);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf %lf",&x[i],&y[i]);
	}
	int u[maxn],v[maxn];
	int k=0;
	while(scanf("%d%d",&u[k],&v[k])&&(!(u[k]==0&&v[k]==0))){
		k++;
	}
	scanf("%lf%lf",&x[0],&y[0]);
	scanf("%lf%lf",&x[n+1],&y[n+1]);
	double dis;
	//先全部铺上差路 
	for(int i=0;i<=n+1;i++){
		//自己到自己的边权为0 
		G[i][i]=0;
		for(int j=i+1;j<=n+1;j++){
			dis=dist(i,j) ;
			G[j][i]=G[i][j]=ncD*dis;
			
		}
	}
	//再铺上好路 
	for(int i=0;i<k;i++){
		dis = dist(u[i],v[i]);
		G[v[i]][u[i]]=G[u[i]][v[i]]=dis*ncR;	
	} 
	DFS(0,0);
	printf("%.4lf",minTot);
}

方法二:
思想: Floyd算法,计算全源最短路径

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
const int INF=10000000;
double ncD,ncR;
int n;
double x[maxn],y[maxn],G[maxn][maxn];
int stX,stY,edX,edY;
double minTot=10000000;
bool visit[maxn]={false};
double dist(int i,int j){
	//下标为i,j的两点 
	return  sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
int main(){
//	freopen("in.txt","r",stdin);
// 	freopen("out.txt","w",stdout);
	fill(G[0],G[0]+maxn*maxn,INF);
	scanf("%lf%lf",&ncD,&ncR);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf %lf",&x[i],&y[i]);
	}
	int u[maxn],v[maxn];
	int k=0;
	while(scanf("%d%d",&u[k],&v[k])&&(!(u[k]==0&&v[k]==0))){
		k++;
	}
	scanf("%lf%lf",&x[0],&y[0]);
	scanf("%lf%lf",&x[n+1],&y[n+1]);
	double dis;
	//先全部铺上差路 
	for(int i=0;i<=n+1;i++){
		//自己到自己的边权为0 
		G[i][i]=0;
		for(int j=i+1;j<=n+1;j++){
			dis=dist(i,j) ;
			G[j][i]=G[i][j]=ncD*dis;
			
		}
	} 
	//再铺上好路 
	for(int i=0;i<k;i++){
		dis = dist(u[i],v[i]);
		G[v[i]][u[i]]=G[u[i]][v[i]]=dis*ncR;	
	} 
	//---------------------------- 
	//Floyd算法核心 
	for(int k=0;k<=n+1;k++){
		for(int i=0;i<=n+1;i++){
			for(int j=0;j<=n+1;j++){
				G[i][j]=min(G[i][k]+G[k][j],G[i][j]);
			}
		}
	}
	//也可以这样写 ----------------------------
	//第三层改一下,运行更快一些 
//	for(int k=0;k<=n+1;k++){
//		for(int i=0;i<=n+1;i++){
//			for(int j=i+1;j<=n+1;j++){
//				G[j][i]=G[i][j]=min(G[i][k]+G[k][j],G[i][j]);
//			}
//		}
//	}
	printf("%.4lf",G[0][n+1]);
}

方法三:
思想: Dijkstra,计算单源最短路径
其中Dijkstra函数是套的模板

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
const int INF=1000000000;
double ncD,ncR;
int n;
double x[maxn],y[maxn],G[maxn][maxn],d[maxn];
int stX,stY,edX,edY;
double minTot=10000000;
bool visit[maxn]={false};
double dist(int i,int j){
	//下标为i,j的两点 
	return  sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
void Dijkstra(int s){
	fill(d,d+maxn,INF);
	d[s]=0;
	for(int i=0;i<=n+1;i++){
		//n+1个点依次访问,执行n+1次 
		int u=-1,MIN=INF;
		for(int j=0;j<=n+1;j++){
			//找到未访问的d[j]最小点 
			if(visit[j]==false&&d[j]<MIN){
				u=j;
				MIN=d[j];
			}
		}
		if(u==-1) return;
		visit[u]=true;
		for(int j=0;j<=n+1;j++){
			//以u为中介,对未访问过的点进行路径优化
			if(visit[j]==false&&G[u][j]!=INF&&d[u]+G[u][j]<d[j]){
				d[j]=d[u]+G[u][j];
			} 
		}
	}
} 
int main(){
// 	freopen("in.txt","r",stdin);
// 	freopen("out.txt","w",stdout);
	fill(G[0],G[0]+maxn*maxn,INF);
	scanf("%lf%lf",&ncD,&ncR);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf %lf",&x[i],&y[i]);
	}
	int u[maxn],v[maxn];
	int k=0;
	while(scanf("%d%d",&u[k],&v[k])&&(!(u[k]==0&&v[k]==0))){
		k++;
	}
	scanf("%lf%lf",&x[0],&y[0]);
	scanf("%lf%lf",&x[n+1],&y[n+1]);
	double dis;
	//先全部铺上差路 
	for(int i=0;i<=n+1;i++){
		//自己到自己的边权为0 
		G[i][i]=0;
		for(int j=i+1;j<=n+1;j++){
			dis=dist(i,j) ;
			G[j][i]=G[i][j]=ncD*dis;
			
		}
	} 
	//再铺上好路 
	for(int i=0;i<k;i++){
		dis = dist(u[i],v[i]);
		G[v[i]][u[i]]=G[u[i]][v[i]]=dis*ncR;	
	} 
	Dijkstra(0);
	printf("%.4lf",d[n+1]);
}

第五题

P1048 采药

考点:动态规划,记忆化搜索
代码说明:
自己写的DFS算法,含剪枝操作,但是最后30分,七个点超时,说明这个题目只能用动态规划
代码思想:
先按价值时间比排序,再深搜剪枝,并不行

#include<bits/stdc++.h>
int T,M;
//int t[110],w[110];
//int f[110][2],t[110][2];
struct Node{
	int t,v;
	float f;
}node[120];
bool cmp(Node a,Node b){
	return a.f>b.f;
}
int maxValues;
void DFS(int s,int tSpend,int nowValues){
	if(s==M) return ;
	else {
		if(tSpend+node[s].t<=T){
			if(nowValues+node[s].v>maxValues) maxValues= nowValues+node[s].v;
			DFS(s+1,node[s].t+tSpend,nowValues+node[s].v);
		} 
		DFS(s+1,tSpend,nowValues);
	}
}
int main(){
	scanf("%d%d",&T,&M);
	for(int i=0;i<M;i++){
		scanf("%d%d",&node[i].t,&node[i].v);
		node[i].f=1.0*node[i].v/node[i].t;
	}
	sort(node,node+M,cmp);
	DFS(0,0,0);
	printf("%d",maxValues);
}

以下dp方法:
方法一:
从第1株开始选
f[i][j] 代表选取到前i株(含),在使用时间j的情况下的能得到的最大价值
常规dp方法

#include <iostream>
#include <cstdio>
#define M 110

using namespace std;

int m,t,num=0,ans;
int f[M][M];
int w[M]; //时间
int v[M]; //价值

int main() {
    scanf("%d%d",&t,&m); //T代表总共能够用来采药的时间,M代表山洞里的草药的数目
    for(int i=1; i<=m; i++) {
        scanf("%d%d",&w[i],&v[i]);//采摘某株草药的时间和这株草药的价值
    }
    for(int i=1; i<=m; i++) { //当前选到第几株
        for(int j=t; j>=0; j--) { //从时间t开始遍历,得到任意时间前i株可以得到的最大价值
            f[i][j]=f[i-1][j];//如果不选第i株
            if(j>=w[i]) { //如果可以选择第i株
            //如果选了,则前i-1株只能使用j-w[i]的时间
            //w[i]的时间用来获取第i株的价值
                f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);
            }

        }
    }
    printf("%d",f[m][t]);
    return 0;
}

方法二
利用一维数组,每次时间都从T开始缩减至t[i],小于t[i]的时间的价值延用之前的那个时间下的最大价值。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1005;
int t,m,w[105]={0},v[105]={0},f[maxn]={0};
int main()
{
	scanf("%d%d",&t,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&w[i],&v[i]);
	}
	for(int i=1;i<=m;i++)
	{
		
		for(int c=t;c>=w[i];c--)
		{
			f[c]=max(f[c],f[c-w[i]]+v[i]);
		}
	}
	printf("%d\n",f[t]);
	return 0;
 } 

第六题

P1195 口袋的天空
考点: 最小生成树(kruskal),并查集,排序

/*---------5-------*/
//将m条连接信息按cost进行排序,从低到高,依次取这些边
//这个过程中不能出现环,连通分量降到k,结束 
// 
const int maxn=1010;
const int INF=1000000000;
int n,m,k,cnt,ans;
int f[maxn];
int findFather(int x){
	if(x==f[x]) return x;
	else return f[x]=findFather(f[x]);
}
struct edge{
	int x,y,c;
}E[10010];
bool cmp(Node a,Node b){
	return a.c<b.c;
}
void Kruskal(){
	sort(E,E+m,cmp);
	cnt=n;
	//初始化 
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	for(int i=0;i<m;i++){
		int fa=findFather(E[i].x);
		int fb=findFather(E[i].y);
		if(fa!=fb){
			f[fa]=fb;
			cnt--;
			ans+=E[i].c;
			if(cnt==k) break;
		}
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=0;i<m;i++){
		scanf("%d%d%d",&E[i].x,&E[i].y,&E[i].c);
	} 
	Kruskal();
	if(cnt==k) printf("%d",ans);
	else printf("No Answer");
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值