• HZOJ 那一天她离我而去


    一个数据水到不行的题,各路大佬用各种方法A掉了这个题(比如A*,最短路,dfs……)。

    这里只说一下我的暴力和被碾压的正解。

    暴力AC系列:

    要找过1点的最小环,那么这个环可以拆成两部分,与1相连的两点经过1的距离和不过一的最短路,那么我们就可以将1的入边截断(出边当然也可以截断,这里是为了方便枚举)并记录这些点到1的距离,枚举与1相连的点,对于每个点跑最短路,再次枚举每个点,ans取min即可。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<queue>
     5 #define LL long long
     6 #define MAXN 10010
     7 #define min(a,b) ((a)<(b)?(a):(b))
     8 #define ma(x) memset(x,0,sizeof(x))
     9 #define MP(a,b) make_pair(a,b)
    10 using namespace std;
    11 struct edge
    12 {
    13     int u,v,w,nxt;
    14     #define u(x) ed[x].u
    15     #define v(x) ed[x].v
    16     #define w(x) ed[x].w
    17     #define n(x) ed[x].nxt
    18 }ed[MAXN*8];
    19 int first[MAXN],num_e;
    20 #define f(x) first[x]
    21 int T,n,m;
    22 const int t=10001;
    23 int dis[MAXN];
    24 bool v[MAXN];
    25 void dist(int st)
    26 {
    27     memset(dis,0x7f,sizeof(dis));ma(v);
    28     dis[st]=0;
    29     priority_queue<pair<int,int> >q;
    30     q.push(MP(0,st));
    31     while(!q.empty())
    32     {
    33         int k=q.top().second;q.pop();
    34         if(v[k])continue;v[k]=1;
    35         for(int i=f(k);i;i=n(i))
    36         if(dis[v(i)]>dis[k]+w(i))
    37             dis[v(i)]=dis[k]+w(i),
    38             q.push(MP(-dis[v(i)],v(i)));
    39     }
    40 }
    41 int tem[MAXN];
    42 inline void add(int u,int v,int w);
    43 signed main()
    44 {
    45     cin>>T;
    46     while(T--)
    47     {
    48         memset(tem,0x7f,sizeof(tem));
    49         ma(first);num_e=0;
    50         cin>>n>>m;
    51         int u,v,d;
    52         for(int i=1;i<=m;i++)
    53         {
    54             cin>>u>>v>>d;
    55             if(u!=1)add(v,u,d);
    56             if(v!=1)add(u,v,d);
    57             if(u==1)tem[v]=d;
    58             if(v==1)tem[u]=d;
    59         }
    60         LL ans=0x7fffff;
    61         for(int i=f(1);i;i=n(i))
    62         {
    63             dist(v(i));
    64             for(int j=f(1);j;j=n(j))
    65             if(v(i)!=v(j))
    66                 ans=min(ans,dis[v(j)]+tem[v(j)]+tem[v(i)]);
    67         }
    68         printf("%lld
    ",ans==0x7fffff?-1:ans);
    69     }
    70 }
    71 inline void add(int u,int v,int w)
    72 {
    73     ++num_e;
    74     u(num_e)=u;
    75     v(num_e)=v;
    76     w(num_e)=w;
    77     n(num_e)=f(u);
    78     f(u)=num_e;
    79 }
    View Code

    正解:

    这题如果数据做的厉害一点肯定也是一个巨坑的题,正解大概和上边的暴力思路相似,只是优化了一点,将与1相连的点分组(分组方法一会再说),一组作为起点,建立超级源点向这一组每个点连边权为0的边,建立超级汇点,从另外一组每个点向超级汇点连边权为0的边,跑最短路即可。如果我们可以保证最终答案的起点与终点分到了两组,求出的就是正确答案。

    接下来说nb的分组方法:

    将每个节点按照编号转化为二进制数,从第0位开始,编号为0的分一组,编号为1的分另一组,这样也就跑了十边最短路的样子。

    但这样能确保最后答案的起点和终点分到两个组吗?显然可以,因为起点和终点的编号不同,二进制为至少有一位不同,也就至少有一次被分到不同组。

    然而正解的代码好像并没有人打……

  • 相关阅读:
    C#中datatabel导出excel(三种方法)
    JDBC 使用说明
    c# lock (obj) 与 lock (this) 区别
    步步深入MySQL:架构>查询执行流程>SQL解析顺序
    SqlParameter的作用与用法
    c# winform窗口自适应各种分辨率类
    SQL中一个很好用的日期格式化函数
    C#生成缩略图
    设置VMware随系统开机自动启动并引导虚拟机操作系统
    在编写PL/SQL代码中使用SELECT语句时如何避免例外发生
  • 原文地址:https://www.cnblogs.com/Al-Ca/p/11219277.html
Copyright © 2020-2023  润新知