题目列表:
第四题
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");
}