20200923day19 刷题记录

1 P1967 货车运输

一道十分综合的题目。存图,最小生成树,并查集,lca,权值更新于一体。

题面

题目描述

A国有(n)座城市,编号从1到(n),城市之间有(m)条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有(q)辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入格式

输入文件第一行有两个用一个空格隔开的整数(n,m),表示A国有(n)座城市和(m)条道路。接下来(m)行每行3个整数(x,y,z),每两个整数之间用一个空格隔开,表示从(x)号城市到(y)号城市有一条限重为(z)的道路。

注意:(x)不等于(y),两座城市之间可能有多条道路。

接下来一行有一个整数(q),表示有(q)辆货车需要运货。接下来(q)行,每行两个整数(x,y),之间用一个空格隔开,表示一辆货车需要从(x)城市运输货物到(y)城市,注意:(x)不等于(y)

输出格式

输出共有(q) 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

输入样例

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

输出样例

3
-1
3

题解

分步解释

1 基本思路

首先题目给出的是一个图,这当中每两个点之间可能有多条路径,但是我们想要限重最大,所以说我们应该寻找任意两点之间限重之和最小的,换言之第一步我们要求出图的最小生成树。(此处用到了两个结构体)

使用kruskal求最小生成树(此处用到了并查集)之后,就可以用lca寻找任意两点的最近公共祖先,并更新最小权值。仅仅是在lca的代码中加入了边权的处理。

2 准备工作

int cnt,deep[500005],f[500005],fa[500005][21],w[500005][21];
bool vis[500005];
struct node{
	int to,next,val;
}edge[500005<<1];
struct way{
	int x,y,dis;
}tu[500005];
int head[500005];
int n,m,q;
int x,y,z;

其中
deep[]记录深度,f[]维护连通性(并查集),fa[]表示点的祖先(lca),head[]链前,w[]表示最大载重
node结构体edge[]表示最小生成树的图,way结构体的tu[]数组表示原图

3 链前实现+并查集实现

这俩没啥好说的

void add(int x,int y,int val){
	edge[++cnt].to=y;
	edge[cnt].val=val;
	edge[cnt].next=head[x];
	head[x]=cnt;
}
int find(int x){
	if(f[x]!=x) f[x]=find(f[x]);
	return f[x];
}
//其中一部分
void update(){
      int gg1=find(tu[i].x);int gg2=find(tu[i].y);
	 if(f[gg1]!=f[gg2]){
	      	f[gg1]=gg2;
	 }
}

4 kruskal

bool cmp(way a,way b){
	return a.dis>b.dis;
}
void krs(){
	sort(tu+1,tu+m+1,cmp);//边权排序
	for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1;i<=m;i++){
		int gg1=find(tu[i].x);int gg2=find(tu[i].y);
		if(f[gg1]!=f[gg2]){
			f[gg1]=gg2;
			add(tu[i].x,tu[i].y,tu[i].dis);
			add(tu[i].y,tu[i].x,tu[i].dis);
		}
	}
	return ;
}

5 lca

void dfs(int x){
	vis[x]=true;
	for(int i=head[x];i;i=edge[i].next){
		int too=edge[i].to;
		if(vis[too]) continue;
		deep[too]=deep[x]+1;
		fa[too][0]=x;
		w[too][0]=edge[i].val;
		
		dfs(too);
	}
	return ;
}
#define INF 999999999
int lca(int x,int y){
	if(find(x)!=find(y)) return -1;
	int ans=INF;
	if(deep[x]>deep[y]) swap(x,y);
	for(int i=20;i>=0;i--){
		if(deep[fa[y][i]]>=deep[x]){
			ans=min(ans,w[y][i]);
			y=fa[y][i];
		}
	}
	if(x==y) return ans;
	for(int i=20;i>=0;i--){
		if(fa[x][i]!=fa[y][i]){
			ans=min(ans,min(w[x][i],w[y][i]));
			x=fa[x][i];y=fa[y][i];
		}
	}
	ans=min(ans,min(w[x][0],w[y][0]));
	//fa[x][0],fa[y][0]即为公共祖先
	return ans;
}
for(int i=1;i<=n;i++){
		if(!vis[i]){
			deep[i]=1;
			dfs(i);
			fa[i][0]=i;
			w[i][0]=INF;
		}
	}
	//LCA初始化
	for(int i=1;i<=20;i++){
		for(int j=1;j<=n;j++){
			fa[j][i]=fa[fa[j][i-1]][i-1];
			w[j][i]=min(w[j][i-1],w[fa[j][i-1]][i-1]);
		}
	}

lca这里有一个权值的更新,其他的都一样

完整代码

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
int cnt,deep[500005],f[500005],fa[500005][21],w[500005][21];
bool vis[500005];
struct node{
	int to,next,val;
}edge[500005<<1];
struct way{
	int x,y,dis;
}tu[500005];
int head[500005];
void add(int x,int y,int val){
	edge[++cnt].to=y;
	edge[cnt].val=val;
	edge[cnt].next=head[x];
	head[x]=cnt;
}
bool cmp(way a,way b){
	return a.dis>b.dis;
}
int find(int x){
	if(f[x]!=x) f[x]=find(f[x]);
	return f[x];
}
int n,m,q;
void krs(){
	sort(tu+1,tu+m+1,cmp);//边权排序
	for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1;i<=m;i++){
		int gg1=find(tu[i].x);int gg2=find(tu[i].y);
		if(f[gg1]!=f[gg2]){
			f[gg1]=gg2;
			add(tu[i].x,tu[i].y,tu[i].dis);
			add(tu[i].y,tu[i].x,tu[i].dis);
		}
	}
	return ;
}
void dfs(int x){
	vis[x]=true;
	for(int i=head[x];i;i=edge[i].next){
		int too=edge[i].to;
		if(vis[too]) continue;
		deep[too]=deep[x]+1;
		fa[too][0]=x;
		w[too][0]=edge[i].val;
		
		dfs(too);
	}
	return ;
}
#define INF 999999999
int lca(int x,int y){
	if(find(x)!=find(y)) return -1;
	int ans=INF;
	if(deep[x]>deep[y]) swap(x,y);
	for(int i=20;i>=0;i--){
		if(deep[fa[y][i]]>=deep[x]){
			ans=min(ans,w[y][i]);
			y=fa[y][i];
		}
	}
	if(x==y) return ans;
	for(int i=20;i>=0;i--){
		if(fa[x][i]!=fa[y][i]){
			ans=min(ans,min(w[x][i],w[y][i]));
			x=fa[x][i];y=fa[y][i];
		}
	}
	ans=min(ans,min(w[x][0],w[y][0]));
	//fa[x][0],fa[y][0]即为公共祖先
	return ans;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		tu[i].x=x;
		tu[i].y=y;
		tu[i].dis=z;
	}
	krs();
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			deep[i]=1;
			dfs(i);
			fa[i][0]=i;
			w[i][0]=INF;
		}
	}
	//LCA初始化
	for(int i=1;i<=20;i++){
		for(int j=1;j<=n;j++){
			fa[j][i]=fa[fa[j][i-1]][i-1];
			w[j][i]=min(w[j][i-1],w[fa[j][i-1]][i-1]);
		}
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d
",lca(x,y));
	}
	return 0;
}

2 1090 砝码称重

一道基础的dp题。主要是想状态转移。

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
int sum; 
int dp[10005];
int a[10];
int weight[10]={0,1,2,3,5,10,20};
int ans;
//dp[i]=0表示质量为i的情况目前没有称出 ,dp[i]=1表示质量为i的已经称出
//处理第j个砝码时质量为w_j,有dp[i-w_j]=1-->d[i]=1;(意思是当且仅当i-w_j称出,i可称出) 
void input(){//输入每个砝码的数量,并求所有砝码的总质量sum 
	int i=1;
	sum=0;
	for(int i=1;i<=6;i++){
		scanf("%d",&a[i]);
		sum+=a[i]*weight[i];
	}
}
void dxp(){
	dp[0]=1;
	for(int i=1;i<=6;i++){//砝码种类 
		for(int j=0;j<a[i];j++){//砝码个数 
			for(int k=sum;k>=weight[i];k--){//判断每种质量能否被称出 
				if(dp[k-weight[i]]==1) dp[k]=1;
			}
		}
	}
}
void output(){
	int time=0;
	for(int i=1;i<=sum;i++){
		if(dp[i]==1) time++;
	}
	printf("Total=%d",time);
}
int main(){
	input();
	dxp();
	output();
	return 0;
	
}

3 1088 轮船问题

仍然dp。本质是最长上升子序列,用(O(n^2))算法做的。下次用(O(nlog n))

(O(n^2))的本质上就是如果(a[j]<a[i])(j<i),那么(f[j]=max(f[i]+1,f[j])),(f[i])之前都是1

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

struct way{
	int nor,sou;
}p[5005];
bool cmp(way a,way b){
	return a.nor<b.nor;
}
int n;
int x,y;
void init(){
	scanf("%d%d",&x,&y);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&p[i].nor,&p[i].sou);
	}
	sort(p+1,p+n+1,cmp);
}
int f[5005];//以i结尾的最长上升子序列的长度 
int ans;
void upline(){
	for(int i=1;i<=n;i++){
		f[i]=1;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			if(p[i].sou>p[j].sou) f[i]=max(f[j]+1,f[i]);
		}
	}
	for(int i=1;i<=n;i++){
		ans=max(ans,f[i]);
	}
}
void prt(){
	printf("%d",ans);
}
int main(){
	init();
	upline();
	prt();
	return 0;
}
要做就做南波万
原文地址:https://www.cnblogs.com/liuziwen0224/p/20200923day19-001.html