题目背景:

幻想乡的亡灵公主,西行寺幽幽子,在幻想乡很受欢迎,经常有妖怪来拜访她,但是幽
幽子并不喜欢被打扰,她希望从白玉楼出发,散步之后再回到白玉楼,同时路上遇到的妖怪越少越好(有趣的是道路两边的妖怪数量并不相同,分别从两个方向经过同一条道路遇到的妖怪数量是不同的)。当然,作为冥界的公主,她是不会重复经过同一条道路的。

问题描述:

给定一个有 n 个点 m 条无向边的图,每条无向边最多只能经过一次。
对于边(ui, vi), 从 ui 到 vi 的代价为 ai,从 vi 到 ui 的代价为 bi,其中 ai 和 bi 不一定相等。 求一个包含 1 号点的有向环,使得环上代价之和最小。(保证图中没有重边和自环。)

输入格式:

第一行两个个正整数 n,m,点数和边数。
接下来 m 行,每行四个正整数,ui,vi,ai,bi。
从 ui 到 vi 的代价为 ai,从 vi 到 ui 的代价为 bi 。

输出格式:

输出一行,一个整数,如果有解输出最小代价,否则输出”-1”(不含引号)。

样例数据 1:

zaw.in
3 3
1 2 4 3
2 3 4 2
1 3 1 1
zaw.out
6

样例数据 2:

zaw.in
3 2
1 2 12 5
3 2 10 5
zaw.out
-1

数据范围:
对于前 15% 的数据,3 <= n <= 20, 3 <= m <= 20
对于前 30% 的数据,3 <= n <= 150, 3 <= m <= 2500
对于前 70% 的数据,3 <= n <= 5000, 3 <= m <= 10^4
对于 100% 的数据, 3 <= n <= 3*10^4, 3 <= m <= 10^5
1<=ui, vi <= n,1 <= ai, bi <= 10^4。
保证图中没有重边,即不存在 i <> j,使得 ui = uj,vi = vj。
保证图中没有自环,即 ui <> vi。

【题解】

算法一:
搜索
期望得分: 15 分

算法二:
启发式搜索
期望得分: 15 ~ 100 分

算法三:
枚举从 1 号点出发到达的第一个点 x 和回到 1 号点之前经过的的最后一个点 y。
那么有向环 1 -> x -> y -> 1 的代价为 w(1, x) + dist(x, y) + w(y, 1) 。
其中 dist(x, y)为不经过 1 号点,从 x 到 y 的代价。显然可以用最短路解决。
时间复杂度: O(n * n * SPFA(n,m)) / O(n^3)
期望得分: 30 分

算法四:
我们发现算法三的瓶颈在于求 dist(x, y),其实我们只要求 n 次单源最短路就可以了。
时间复杂度: O(n * SPFA(n,m)) / O(n * n * log n)
期望得分: 70 分

算法五:
我们发现算法四的瓶颈在于枚举 x 和 y。
事实上,我们可以把除 1 号点以外的点分成两个集合 ,X 和 Y。
然后通过一次最短路求出有向环 1 -> x ∈ X -> y ∈ Y -> 1 的最小代价。
那么按二进制位分组,考虑二进制下第 i 位。
1.第 i 位为 0 的点划分到 X 集合,第 i 位为 1 的点划分到 Y 集合,然后跑最短路更新答案。
2.第 i 位为 1 的点划分到 X 集合,第 i 位为 0 的点划分到 Y 集合,然后跑最短路更新答案。
我们知道只要任意点对都至少被分别划分到两个集合中一次,就可以得到答案。
而两个数至少有一个二进制位不相同,所以任意点对都至少被划分一次。
时间复杂度: O(log n * SPFA(n, m)) / O(n * log n * log n)
期望得分:100 分

不过经试验,出题人题解的满分算法会TLE到2.33s,但出题人的算法4反而在删掉没用东西后能AC。。。

找到节点1所有的相邻节点,dis[t]初值设为a[x].w,然后跑一遍t->1的SPFA,加上最优性剪枝即可。

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define fp(i,a,b) for(int i=a;i<=b;i++)
#define fq(i,a,b) for(int i=a;i>=b;i--)
#define il inline
#define ll long long 
using namespace std;
const int maxN=30005;
const int maxM=100001*4;
const int inf=214748364;
struct Edge
{
    int u,v,w,next;
}e[maxM];
int n,m,ans=inf,cnt,head,dis[maxN],q[maxM*10],tail,h[maxN];
bool inq[maxN];
il int gi()
{
   int x=0;
   short int t=1;
   char ch=getchar();
  while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  if(ch=='-') t=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  return x*t;
}
void add(int u,int v,int w)
{
    e[++cnt]=(Edge){u,v,w,h[u]};h[u]=cnt;
}
il void spfa(int st,int initw)//模板
{
    memset(dis,127,sizeof(dis));
    memset(inq,0,sizeof(inq));
    dis[st]=initw;
    q[tail++]=st;
    inq[st]=1;
    while(head<tail)
    {

        int u=q[head++];
        inq[u]=0;
        if(dis[u]>=ans)
        {
            inq[u]=0;
            continue;
        }
        if(u==1)
        {
            ans=dis[u];
            inq[u]=0;
            continue;
        }//最优性剪枝
       for(int i=h[u];i!=-1;i=e[i].next)
        {
             int v=e[i].v;
            if(u==st&&v==1) continue;
            if(dis[u]+e[i].w<dis[v])
            {
                dis[v]=dis[u]+e[i].w;
                if(!inq[v])
                {

                    q[tail++]=v;
                    inq[v]=1;
                }
            }
        }
    }
}
int main()
{
    freopen("zaw.in","r",stdin);
    freopen("zaw.out","w",stdout);
    memset(h,-1,sizeof(h));
    n=gi();m=gi();
    fp(i,1,m)
    {
        int u=gi(),v=gi(),a=gi(),b=gi();
        add(u,v,a);
        add(v,u,b);
    }
    ans=inf;
    for(int i=h[1];i!=-1;i=e[i].next)
        spfa(e[i].v,e[i].w);
    if(ans==inf) printf("-1\n");
    else printf("%d\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

最后,把大佬的程序(也是跑的最快的)放在这里,看到时候是否能看懂。

// MADE BY QT666
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=500050;
const int Inf=19260817;
int cnt=1,n,m,head[N],to[N],nxt[N],c[N],in[N],vis[N],ans=Inf,vis2[N];
int W[N];
struct Data{
  int fir,sec,ffrom,sfrom;
}dis[N];
struct data{
  int x,fa,from;
}q[N*2];
void lnk(int x,int y,int v,int z){
  to[++cnt]=y,c[cnt]=v,nxt[cnt]=head[x],head[x]=cnt;
  to[++cnt]=x,c[cnt]=z,nxt[cnt]=head[y],head[y]=cnt;
}
void spfa(){
  int t=0,sum=1;q[0].x=1;dis[1].fir=dis[1].sec=0;
  while(t<sum){
    data now=q[t++];
    for(int i=head[now.x];i;i=nxt[i]){
      if(i!=(now.fa^1)||now.fa==0){
    int y=to[i],flag=0;
    if(now.x!=1){
      if(dis[now.x].fir+c[i]<dis[y].fir){
        if(dis[y].fir<dis[now.x].sec+c[i]){
          if(dis[y].ffrom!=dis[now.x].ffrom){
        dis[y].sec=dis[y].fir;dis[y].sfrom=dis[y].ffrom;
        dis[y].fir=dis[now.x].fir+c[i];dis[y].ffrom=dis[now.x].ffrom;
        flag=1;
          }
          else{
        if(dis[y].sec<dis[now.x].sec){
          dis[y].fir=dis[now.x].fir+c[i];
          dis[y].ffrom=dis[now.x].ffrom;flag=1;
        }
        else{
          if(dis[y].ffrom!=dis[now.x].sfrom){
            dis[y].fir=dis[now.x].fir+c[i];
            dis[y].ffrom=dis[now.x].ffrom;
            dis[y].sec=dis[now.x].sec+c[i];
            dis[y].sfrom=dis[now.x].sfrom;flag=1;
          }
        }
          }
        }
        else{
          dis[y].fir=dis[now.x].fir+c[i];
          dis[y].ffrom=dis[now.x].ffrom;
          dis[y].sec=dis[now.x].sec+c[i];
          dis[y].sfrom=dis[now.x].sfrom;
        }
      }
      else {
        if(dis[y].sec<dis[now.x].sec+c[i]&&dis[y].sec>dis[now.x].fir+c[i]){
          if(dis[y].ffrom!=dis[now.x].ffrom){
        dis[y].sec=dis[now.x].fir+c[i];
        dis[y].sfrom=dis[now.x].ffrom;flag=1;
          }
        }
        else if(dis[y].sec>dis[now.x].sec+c[i]){
          if(dis[y].ffrom!=dis[now.x].ffrom){
        dis[y].sec=dis[now.x].fir+c[i];
        dis[y].sfrom=dis[now.x].ffrom;flag=1;
          }
          else if(dis[y].ffrom!=dis[now.x].sfrom){
        dis[y].sec=dis[now.x].sec+c[i];
        dis[y].sfrom=dis[now.x].sfrom;flag=1;
          }
        }
      }
    }
    if(flag||now.x==1) q[sum++]=(data){y,i};
      }
    }
  }
}
int main(){
  freopen("zaw.in","r",stdin);
  freopen("zaw.out","w",stdout);
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++){
    int x,y,v,z;scanf("%d%d%d%d",&x,&y,&v,&z);
    lnk(x,y,v,z);
  }
  for(int i=2;i<=n;i++) dis[i].fir=dis[i].sec=Inf;
  for(int i=head[1];i;i=nxt[i]){
    if(dis[to[i]].fir==Inf){
      dis[to[i]].fir=c[i];
      dis[to[i]].ffrom=i;
    }
    else{
      if(dis[to[i]].fir>c[i]){
    dis[to[i]].sec=dis[to[i]].fir;
    dis[to[i]].sfrom=dis[to[i]].ffrom;
    dis[to[i]].fir=c[i];
    dis[to[i]].ffrom=i;
      }
      else{
    if(dis[i].sec>c[i]){
      dis[to[i]].sec=c[i];
      dis[to[i]].sfrom=i;
    }
      }
    }
  }
  spfa();
  for(int i=head[1];i;i=nxt[i]){
    int y=to[i];
    if(dis[y].ffrom!=i) ans=min(ans,dis[y].fir+c[i^1]);
    else ans=min(ans,dis[y].sec+c[i^1]);
  }
  if(ans==Inf) puts("-1");
  else printf("%d",ans);
}
posted @ 2017-08-21 21:41  小蒟蒻ysn  阅读(123)  评论(0编辑  收藏  举报