6.14总结
题目一:B3611 【模板】传递闭包
思路总结
- 问题本质:给定有向图的邻接矩阵,求传递闭包矩阵(任意两点间是否存在直接或间接路径)。
- 核心算法:使用 Floyd-Warshall 算法 的变种:
- 初始化距离矩阵
dp:若存在边(i, j),则dp[i][j] = 1;否则为极大值(0x3f3f3f3f)。 - 三重循环枚举中间点
k,更新dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j])(实际只需判断是否可达)。
- 初始化距离矩阵
- 输出转换:若
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
思路总结
- 问题本质:在偏序关系中,求还需添加的最少比较次数,使所有奶牛全序。
- 核心算法:
- Floyd 求传递闭包:
dp[i][j]表示i是否强于j。 - 统计不确定关系:遍历所有点对
(i, j),若dp[i][j]和dp[j][i]均为0(关系不确定),则计数。
- Floyd 求传递闭包:
- 答案计算:不确定关系对数 =
(ans - n) / 2(ans为总不确定有序对数,n为对角线条目)。
重难点
- 重点:传递闭包推导间接关系(若
i > k且k > 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
思路总结
- 问题本质:添加一条边连接两个牧场,使新牧场的直径最小。
- 核心步骤:
- Floyd 求最短路径:计算任意两点间最短欧氏距离。
- DFS 标记连通分量:区分不同牧场。
- 计算牧场直径:每个牧场的直径 = 内部点对最短路径的最大值。
- 枚举新边:对分属不同牧场的点
i, j,新直径可能为:- 原两牧场直径的最大值。
- 连接
i, j后形成的新路径长度(dis_i[i] + dist(i, j) + dis_i[j])。
- 答案:所有新直径候选值的最小值。
重难点
- 重点:新牧场的直径由三部分取最大值决定(原牧场直径、新路径)。
- 难点:
- 复杂度的控制: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;
}
本人(KK_SpongeBob)蒟蒻,写不出好文章,但转载请注明原文链接:https://www.cnblogs.com/OIer-QAQ/p/18938593

浙公网安备 33010602011771号