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]/*point-weight*/)$$
然后就用$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 }