zoukankan      html  css  js  c++  java
  • 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;
    }
    
    要做就做南波万
  • 相关阅读:
    风雨中,苦算什么!!!
    痛心疾首+无奈绝望!!!
    PHP页面跳转总结
    Java的HttpClient的实现
    java细节篇(==和equals的区别)
    cmd命令笔记
    Python的HttpClient实现
    常用linux命令
    Go的HttpClient实现
    android问题笔记集
  • 原文地址:https://www.cnblogs.com/liuziwen0224/p/20200923day19-001.html
Copyright © 2011-2022 走看看