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\n",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(n\log 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;
}

浙公网安备 33010602011771号