- 三向城
题目描述
三向城是一个巨大的城市,之所以叫这个名字,是因为城市中遍布着数不尽的三岔路口。(来自取名力为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(i−1)]=dis[i][0]
状态 s 0->1<<(n-2)-1
转移起点 i在状态s中
转移终点 j不在状态s中
f[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);
}