第八周周报

P8794 [蓝桥杯 2022 国 A] 环境治理

代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 100 + 10;
int n, q;
long long d[N][N], l[N][N];
long long tmp[N][N];

bool check(long long val) {
	long long sum = 0;
	for (int i = 1; i <= n; i++) {
		long long t = val / n;
		if (val % n + 1 > i) t++;
		for (int j = 1; j <= n; j++) {
			tmp[i][j] = max(l[i][j], d[i][j] - t);
		}
	}
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++) 
			for (int j = 1; j <= n; j++)
				tmp[i][j] = min(tmp[i][j], tmp[i][k] + tmp[k][j]);
	
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++)
			sum += tmp[i][j];
	}	
	return (sum <= q);
}


int main() {
	cin >> n >>  q;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			cin >> d[i][j];
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			cin >> l[i][j];
	
	long long l = 0, r = 1e5 + 10;
	long long ans = 1e5 + 10;
	while (l <= r) {
		long long mid = (l + r) / 2;
		if (check(mid)) {
			ans = min(ans, mid);
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	
	if (ans > (int)1e5) cout << "-1";
	else cout << ans;
	return 0;
}

解析:

每次二分更新边权,再跑一遍最短路就可以了。

P3367 【模板】并查集

代码:

#include<bits/stdc++.h>
using namespace std;
int i,j,k,n,m,s,ans,f[10010],p1,p2,p3;
//f[i]表示i的集合名
int find(int k){
	//路径压缩
    if(f[k]==k)return k;
    return f[k]=find(f[k]);
}
int main()
{
    cin>>n>>m;
    for(i=1;i<=n;i++)
        f[i]=i;//初始化i的老大为自己
    for(i=1;i<=m;i++){
        cin>>p1>>p2>>p3;
        if(p1==1)
            f[find(p2)]=find(p3);
            //p3打赢了p2
        else
            if(find(p2)==find(p3))
            //是否是一伙的
                printf("Y\n");
            else
                printf("N\n");
    }
    return 0;
}

解析:

模板

P8604 [蓝桥杯 2013 国 C] 危险系数

代码:

#include<bits/stdc++.h>
#define LL long long
#define made return
#define in 0
#define China ;
using namespace std;
LL n,m,u,v,ans,cnt[1010],sum;
bool bj[1010],a[1010][1010];
void dfs(LL now){
	if(now==v){//如果走到终点了, 
		sum++;//路径总数加一。 
		for(int i=1;i<=n;i++)
			if(bj[i]==1)cnt[i]++;//每个被走过的点,被走总次数加一 
	}
	else{
		for(int i=1;i<=n;i++)
			if(a[now][i]==1&&bj[i]==0){//如果两点连通且下一步要走到的点未被走过, 
				bj[i]=1;//标记。
				dfs(i);
				bj[i]=0;//回溯一步。 
			}
	}
}
int main(){
	scanf("%lld%lld",&n,&m);
	while(m--){
		scanf("%lld%lld",&u,&v);
		a[u][v]=a[v][u]=1;//输入邻接矩阵。因为是无向的,所以u到v和v到u都要设为1。 
	}
	scanf("%lld%lld",&u,&v);
	dfs(u);
	if(sum>0){//dfs求解
		for(int i=1;i<=n;i++)
			if(cnt[i]==sum)ans++;//如果这个点被走过的总次数与路径总数相等,那么删去这个点起点与终点间一定不连通。 
		printf("%lld",ans-1);//因为起点也被算在内,所以总危险系数要减去起点的1。 
	}
	else printf("-1");//如果询问的两点无路径连通则输出'-1'。
	made in China 
}
//made in China. 中国制造。

解析:

可以使用 dfs(深度优先搜索)求解,求出 u 到 v 间的每一条路径,将路径总数统计,并将被经过的点被经过总数加一。如果一个点被经过的次数与总路径条数相等,那么这一个点就是 u 和 v 的关键点。

P1330 封锁阳光大学

代码:

#include<bits/stdc++.h>
using namespace std;
int f[10001],a,b,n,m,t[10001],bj[10001],h[10001],ans;
int find(int x)//模板函数;
{
    if(f[x]!=x) f[x]=find(f[x]);
    return f[x];
}
void xx(int x,int y)//判断函数;
{
    int qq=find(x);
    if(qq!=y)//如果他们父亲不相等将他们合并;
    {
        f[y]=qq;
        t[qq]+=t[y];
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
        t[i]=1;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        int x1=find(a),x2=find(b);
        if(x1!=x2)//相邻两点一定异色;
        {
            if(h[a]) xx(h[a],x2);/*a的父亲节点一定
            和a异色,一定和a的异色点同色,所以将他们
            合并;*/
            if(h[b]) xx(h[b],x1);//同上;
            h[a]=x2;//h数组存a点异色点;
            h[b]=x1;//同上;
        }
        else//如果他们同色,表示不行;
        {
            cout<<"Impossible";
            return 0;
        }
    }
    for(int i=1;i<=n;i++)
    {
        int q=find(i);
        if(!bj[q])//表示这个集合的答案还没被选过;
        {
            int q1=find(h[i]);
            bj[q]=1;
            bj[q1]=1;
            ans+=min(t[q],t[q1]);//两种情况最小值;
        }
    }
    cout<<ans;
    return 0;
}

解析:

并查集

P3916 图的遍历

代码:

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

#define MAXL 100010

int N, M, A[MAXL];
vector<int> G[MAXL]; //vector存图 

void dfs(int x, int d) {
	if(A[x]) return; //访问过 
	A[x] = d;
	for(int i=0; i<G[x].size(); i++)
		dfs(G[x][i], d);
}

int main() {
	int u, v;
	scanf("%d%d", &N, &M);
	for(int i=1; i<=M; i++) {
		scanf("%d%d", &u, &v);
		G[v].push_back(u); //反向建边 
	}
	for(int i=N; i; i--) dfs(i, i); 
	for(int i=1; i<=N; i++) printf("%d ", A[i]);
	printf("\n");
	return 0;
}

解析:

按题目来每次考虑每个点可以到达点编号最大的点,不如考虑较大的点可以反向到达哪些点

循环从N到1,则每个点i能访问到的结点的A值都是i

每个点访问一次,这个A值就是最优的,因为之后如果再访问到这个结点那么答案肯定没当前大了

P1119 灾后重建

代码:

#include<iostream>
#include<cstdio>
#define N 205
using namespace std;
int n,m;
int a[N];
int f[N][N];//邻接矩阵存边
inline void updata(int k){
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++)
	if(f[i][j]>f[i][k]+f[j][k])
	f[i][j]=f[j][i]=f[i][k]+f[j][k];//用这个新的更新所有前面的 
	return;
}
int main(){
	cin>>n>>m;
	for(int i=0;i<n;i++)
	scanf("%d",a+i);//依次输入每一个村庄建立完成时需要的时间
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++){
		f[i][j]=1e9;//初始化为保证它不爆炸范围内的最大值 
	}
	for(int i=0;i<n;i++)
	f[i][i]=0;
	int s1,s2,s3;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&s1,&s2,&s3);
		f[s1][s2]=f[s2][s1]=s3;//初始化边长 
	}
	int q;
	cin>>q;
	int now=0;
	for(int i=1;i<=q;i++){//处理各询问 
		scanf("%d%d%d",&s1,&s2,&s3);
		while(a[now]<=s3&&now<n){
			updata(now);//依次更新点,使它可以被用来更新其他的点 
			now++;
		}
		if(a[s1]>s3||a[s2]>s3)cout<<-1<<endl;
		else {
			if(f[s1][s2]==1e9)cout<<-1<<endl;
			else cout<<f[s1][s2]<<endl;
		}
	}
	return 0;
} 

解析:

Floyd算法

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值