三向城+香兰子

91 篇文章 1 订阅
28 篇文章 0 订阅
  1. 三向城
    题目描述
    三向城是一个巨大的城市,之所以叫这个名字,是因为城市中遍布着数不尽的三岔路口。(来自取名力为0的出题人)
    具体来说,城中有无穷多个路口,每个路口有唯一的一个正整数标号。除了1号路口外,每个路口都连出正好3条道路通向另外3个路口:编号为x(x>1)的路口连出3条道路通向编号为x*2,x*2+1和x/2(向下取整)的3个路口。1号路口只连出两条道路,分别连向2号和3号路口。
    所有道路都是可以双向通行的,并且长度都为1。现在,有n个问题:从路口x到路口y的最短路长度是多少?

输入格式
第一行包含一个整数n,表示询问数量;
接下来n行,每行包含两个正整数x, y,表示询问从路口x到路口y的最短路长度。

输出格式
输出n行,每行包含一个整数,表示对每次询问的回答。如果对于某个询问不存在从x到y的路径,则输出-1。

样例输入
3
5 7
2 4
1 1

样例输出
4
1
0

样例解释
5号路口到7号路口的路径为:5->2->1->3->7,长度为4;
2号路口到4号路口的路径为:2->4,长度为1;
1号路口到本身的路径长度为0;

数据范围
对30%的数据,x,y≤20;
对60%的数据,x,y≤10^5,n≤10;
对100%的数据,x,y≤10^9,n≤10^4。

———————————–

暴力向上跳即可。没有技巧233。

#include <cstdio>
#include <iostream>
#include <cstring>
#define ll long long
using namespace std;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x,y,ans=0;
        scanf("%d%d",&x,&y);
        while(x!=y)
        {
            ans++;
            if(y>x) swap(x,y);
            x/=2;
        }
        printf("%d\n",ans);
    }

       return 0;
} 

这里写图片描述
这里写图片描述
这里写图片描述

———————————————

这个范围,状压无误。
但是最关键的是
咋状压?
。。。。。

好吧
正解:状压+Floyd(逃)
首先用Floyd求出任意两点的距离(废话)
F[i][s]表示从家到 i 收割了s状态下的花最少的步数(1收,0不收)
G[i][s]表示从i到花店s状态下的花最少的步数(1收,0不收)
怎么求?
很明显
F[i][2(i1)]=dis[i][0]

状态 s 0->1<<(n-2)-1
转移起点 i在状态s中
转移终点 j不在状态sf[j][s|(1<<j-1)]=min(f[i][s]+dis[i][j])

对于G是一个处理方法。

怎么得到答案?

状态 s 0->1<<(n-2)-1
判断 S中有几个田?
! =(n-2)/2 状态不合法
第一组田终点 i 1->n-2在s中
第二组田起点 j 1->n-2在补集中
答案1=min(f[i][s]+dis[i][j]+g[j][全集^s])
反过来统计
答案2=min(g[i][s]+dis[i][j]+f[j][全集^s])
不能一起吗?
不能!
因为田中的顺序可能是不同的!
对上述答案取min即为最终答案
#include <cstdio>
#include <iostream>
#include <cstring>
#define ll long long
using namespace std;
int dis[21][21];
int f[21][(1<<18)+1],g[21][(1<<18)+1];
int main()
{
    freopen("vanilla.in","r",stdin);
    freopen("vanilla.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    memset(dis,127/3,sizeof(dis));
    memset(f,127/3,sizeof(f));
    memset(g,127/3,sizeof(g));
    for(int i=1;i<=m;i++)
     {
        int x,y,c;
        scanf("%d%d%d",&x,&y,&c);
        dis[x][y]=dis[y][x]=min(dis[x][y],c);
     } 

    for(int i=0;i<n;i++)
     dis[i][i]=0;

    for(int k=0;k<n;k++)
     for(int i=0;i<n;i++)
      for(int j=0;j<n;j++)
       dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);

    if(n==3)
    {
        printf("%d",(dis[0][1]+dis[1][2])*2);
        return 0;
    }
    for(int i=1;i<=n-2;i++)
     f[i][1<<i-1]=dis[0][i],f[i][0]=0,g[i][0]=0,g[i][1<<i-1]=dis[i][n-1];

    for(int s=0;s<1<<n-2;s++)
    {
        for(int i=1;i<=n-2;i++)
         if(s&(1<<i-1))
         {
            for(int k=1;k<=n-2;k++)
             //if((s&(1<<k-1))==0)
              {
                f[k][s|(1<<k-1)]=min(f[k][s|(1<<k-1)],f[i][s]+dis[i][k]);
                g[k][s|(1<<k-1)]=min(g[k][s|(1<<k-1)],g[i][s]+dis[k][i]);
              } 
         }
    }

    /*for(int s=0;s<(1<<n-2);s++)
     for(int i=1;i<=n-2;i++)
      printf("%d\n",f[i][s]);*/
    //printf("%d",f[1][1]);
    int ans=1e8;
    int q=(1<<n-2)-1;
    int c=((n-2)/2);
    if(c==0) c=n-2;
    for(int s=0;s<1<<n-2;s++)
    {
        int num=0;
        for(int i=1;i<=n-2;i++)
         if(s&(1<<i-1)) num++;
        if(num!=c) continue;
        //printf("%d\n",num);
        int sum=1e9;
        for(int i=1;i<=n-2;i++)
        if(s&(1<<i-1))
        {
            int s2=q^s;
            //printf("%d %d\n",s,s2);
            if(s2==0) ans=min(ans,f[i][s]+g[i][s]);
            for(int j=1;j<=n-2;j++)
             //if((s2&(1<<j-1)))
              {
                //printf("yes\n");
                sum=min(sum,f[i][s]+dis[i][j]+g[j][s2]);
              }
        }
        for(int i=1;i<=n-2;i++)
        if(s&(1<<i-1))
        {
            int s2=q^s;
            //printf("%d %d\n",s,s2);
            //if(s2==0) ans=min(ans,f[i][s]+g[i][s]);
            for(int j=1;j<=n-2;j++)
             //if((s2&(1<<j-1)))
              {
                //printf("yes\n");
                int ss=g[i][s]+dis[i][j]+f[j][s2];
                ans=min(ans,ss+sum);
              }
        }
    }

    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值