6.14总结

题目一:B3611 【模板】传递闭包

思路总结

  1. 问题本质:给定有向图的邻接矩阵,求传递闭包矩阵(任意两点间是否存在直接或间接路径)。
  2. 核心算法:使用 Floyd-Warshall 算法 的变种:
    • 初始化距离矩阵 dp:若存在边 (i, j),则 dp[i][j] = 1;否则为极大值(0x3f3f3f3f)。
    • 三重循环枚举中间点 k,更新 dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j])(实际只需判断是否可达)。
  3. 输出转换:若 dp[i][j] 非初始极大值,输出 1(可达),否则输出 0

重难点

  • 重点:理解 Floyd 算法在传递闭包中的应用(动态规划思想)。
  • 难点
    • 状态转移设计:通过中间点更新路径可达性。
    • 初始化细节:非边需初始化为极大值(代表不可达)。
  • 关键点:时间复杂度 (O(n^3))((n \leq 100) 可接受)。

代码

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e3+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int a[maxn][maxn],dp[maxn][maxn];
int n;
void floyd(){
	memset(dp,0x3f3f3f3f,sizeof(dp));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(a[i][j]!=0){
				dp[i][j]=1;
			}
		}
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
			}
		}
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
		}
	}
	floyd();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(dp[i][j]==dp[0][0]){
				cout<<'0'<<" ";
			}
			else{
				cout<<"1 ";
			}
		}
		cout<<endl;
	}
	return 0;
}

题目二:P2881 [USACO07MAR] Ranking the Cows G

思路总结

  1. 问题本质:在偏序关系中,求还需添加的最少比较次数,使所有奶牛全序。
  2. 核心算法
    • Floyd 求传递闭包dp[i][j] 表示 i 是否强于 j
    • 统计不确定关系:遍历所有点对 (i, j),若 dp[i][j]dp[j][i] 均为 0(关系不确定),则计数。
  3. 答案计算:不确定关系对数 = (ans - n) / 2ans 为总不确定有序对数,n 为对角线条目)。

重难点

  • 重点:传递闭包推导间接关系(若 i > kk > j,则 i > j)。
  • 难点
    • 理解不确定关系的定义:既无 i > j 也无 j > i
    • 答案转换:无序对数量 = 不确定有序对数量减去对角线后除以 2。
  • 关键点:Floyd 使用位运算优化(dp[i][j] |= dp[i][k] & dp[k][j])。

代码

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e3+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int a[maxn][maxn];
bool dp[maxn][maxn];
int n,m;
void floyd(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(a[i][j]!=0){
				dp[i][j]=1;
			}
		}
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dp[i][j]|=(dp[i][k]&dp[k][j]);
			}
		}
	}
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		a[u][v]=1;
	}
	floyd();
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(dp[i][j]==0&&dp[j][i]==0){
				ans++;
			}
		}
	}
	cout<<(ans-n)/2;
	return 0;
}    

题目三:P1522 [USACO2.4] 牛的旅行 Cow Tours

思路总结

  1. 问题本质:添加一条边连接两个牧场,使新牧场的直径最小。
  2. 核心步骤
    • Floyd 求最短路径:计算任意两点间最短欧氏距离。
    • DFS 标记连通分量:区分不同牧场。
    • 计算牧场直径:每个牧场的直径 = 内部点对最短路径的最大值。
    • 枚举新边:对分属不同牧场的点 i, j,新直径可能为:
      • 原两牧场直径的最大值。
      • 连接 i, j 后形成的新路径长度(dis_i[i] + dist(i, j) + dis_i[j])。
  3. 答案:所有新直径候选值的最小值。

重难点

  • 重点:新牧场的直径由三部分取最大值决定(原牧场直径、新路径)。
  • 难点
    • 复杂度的控制:Floyd (O(n^3)) 在 (n \leq 150) 下可行。
    • 正确性保证:新路径长度需严格计算(dis_i[i] 是点 i 在其牧场的最大距离)。
  • 关键点:欧氏距离计算与连通块处理。

代码

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e3+5,mod=1e9+7,inf=0x3f3f3f3f3f;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
double dis[maxn][maxn],x[maxn],y[maxn],sz[maxn],dis_i[maxn],maxi=0.0;
int n,vis[maxn];
double get_dis(int a,int b){
	return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
void dfs(int u,int id){
	vis[u]=id;
	for(int i=1;i<=n;i++){
		if(vis[i]==0&&dis[u][i]<inf){
			dfs(i,id);
		}
	}
}
void floyd(){
	for(int i=1;i<=n;i++){
		dis[i][i]=0;
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			}
		}
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x[i]>>y[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			char c;
			cin>>c;
			if(c=='1'){
				dis[i][j]=get_dis(i,j);
			}
			else if(i!=j){
				dis[i][j]=inf;
			}
		}
	}
	int id=1;
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			dfs(i,id);
			id++;
		}
	}
	floyd();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(dis[i][j]<inf){
				dis_i[i]=max(dis_i[i],dis[i][j]);
			}
		}
		sz[vis[i]]=max(sz[vis[i]],dis_i[i]);
	}
	double ans=inf;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(vis[i]!=vis[j]){
				double tmp=max(sz[vis[i]],sz[vis[j]]);
				tmp=max(tmp,dis_i[i]+get_dis(i,j)+dis_i[j]);
				ans=min(tmp,ans);
			}
		}
	}
	cout<<fixed<<setprecision(6)<<ans;
	return 0;
}
posted @ 2025-06-20 15:48  KK_SpongeBob  阅读(23)  评论(0)    收藏  举报