把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

USACO4.1 Fence Loops【最小环&边->点转化】

数据不是很大,如果要转换为正常的那种建图方式的话,可以给点进行标号,用一个二维数组存这两条边相交的那个点的标号,方便处理。一定要注意不要同一个点使用不同的编号也不要不同的点使用同一个编号(这不是废话嘛)不展开。

想多说一下一种比较有意思的做法,就是把边看成点,把边权转化为点权。

这样的话,原本的最小环长就变成了一条路径上经过的所有点的点权之和啦。

大概,张这个样子吧:

 

 

 

边是没有权值的,它只表示连通性。

 

不过由于把边看成了点,所以处理有些特殊:

原本的dis[i][j]是指点i到点j所经过的边权之和的最小值,但边->点之后,就变成了点i到点j所经过的点权之和的最小值(含点i和点j),那么在转移的时候,原本的方程式dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])就不能用,因为dis[i][k]dis[k][j]都包含了点k的点权,所以要减去,应该写成这个样子:

dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]pw[k]/pointweight/)

然后就用floyd求最小环就可以了

->break in something

简单说一下floyd求最小环,感觉就是做一遍floyd答案就是dis[i][i]

但是不行啊,因为无法保证dis[i][k]dis[k][j]表示的路是不一样的。

转移的时候是枚举i,j,k三重循环的嘛,就在每一次通过k转移之前(也就是现在的dis[i][j]是靠1 k作为中转站转移过来的)对最小环进行更新,因为dis[i][j]i>k>j走的路肯定不一样(一个肯定没有经过k,一个肯定经过了k

就可以啦。

(如果你会floyd的话,上面的应该都能看懂,不过不会的话可以百度一下,floyd不是很难)

->end

另外,有一个很坑的地方,是由于边->点造成的,如果边长成这个样子:

 

 

那把边搞成点就成了这个样子:

 

 

 

 这是一个环,但是显然这个环是不符合题目要求的,因为题目要的是这种环:

 

 

 

 

特判一下就可以啦,由于N只有100,随便怎么搞都可以。

复制代码
 1 /*
 2 ID: Starry21
 3 LANG: C++
 4 TASK: fence6         
 5 */
 6 #include<cstdio>
 7 #include<algorithm>
 8 #include<cstring>
 9 using namespace std;
10 #define N 105
11 #define INF 0x3f3f3f3f
12 int n,len[N]/*新图点权*/,ans=INF;
13 int dis[N][N]/*两点间最短距离(floyd)*/,s[N][N]/*直接距离(直接相连的)*/;
14 bool G[N][N]/*邻接矩阵*/,vis[N][N][N]/*判断三条边是否交于一点*/;
15 int tmp[N],ns[5];//辅助输入,见程序 
16 int main()
17 {
18     //freopen("fence6.in","r",stdin);
19     //freopen("fence6.out","w",stdout);
20     scanf("%d",&n);
21     for(int p=1;p<=n;p++)
22     {
23         int id;scanf("%d",&id);
24         tmp[0]=id;
25         scanf("%d %d %d",&len[id],&ns[1],&ns[2]);
26         for(int q=1;q<=2;q++)
27         {
28             for(int i=1;i<=ns[q];i++)
29             {
30                 int id2;scanf("%d",&id2);
31                 G[id][id2]=G[id2][id]=1;
32                 tmp[i]=id2;//记录边交于一点的编号
33                 for(int j=1;j<i;j++)
34                     for(int k=0;k<j;k++)
35                         vis[tmp[i]][tmp[j]][tmp[k]]=vis[tmp[i]][tmp[k]][tmp[j]]=vis[tmp[j]][tmp[i]][tmp[k]]=vis[tmp[j]][tmp[k]][tmp[i]]=vis[tmp[k]][tmp[i]][tmp[j]]=vis[tmp[k]][tmp[j]][tmp[i]]=1;
36             }
37         }
38     }
39     memset(dis,0x3f,sizeof(dis));
40     for(int i=1;i<=n;i++)
41         for(int j=i+1;j<=n;j++)
42             if(G[i][j])
43                 dis[i][j]=dis[j][i]=s[i][j]=s[j][i]=len[i]+len[j];
44     for(int k=1;k<=n;k++)
45     {
46         for(int i=1;i<=n;i++)
47             if(G[i][k])
48                 for(int j=i+1;j<=n;j++)
49                     if(G[k][j]&&!vis[i][j][k])
50                         ans=min(ans,dis[i][j]+s[i][k]+s[k][j]-len[i]-len[j]-len[k]);
51         for(int i=1;i<=n;i++)
52             for(int j=1;j<=n;j++)
53                 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]-len[k]);
54     }
55     printf("%d\n",ans);
56     return 0;
57 }
Code
复制代码

 

 

 

 

 

posted @   Starlight_Glimmer  阅读(144)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示