Codeforces Round #228 Div1 B Fox and Minimal path

来源:互联网 发布:win10能用的编程软件 编辑:程序博客网 时间:2024/05/18 07:29

    这道题的意思大致就是给你一个数k(k<=10^9),要你用不超过1000个点构建一张图,使得这张图中从1号点到n号点刚好有k条最短路。

    想法很简单,乘法原理加加法原理就可以解决了。


    我的想法就是下面这张图:

    比如,上面这张图就是k=31=11111的图,我们把k化为2进制后,从左到右,先加

1—>3这条边,然后加3—>4,3—>5,4—>5,5—>6这四条边,就将总的方案数乘以了2,得到了2=10的图,再加上1—>6的这一条边,方案数+1,变为了3=11的方案数,继续将11左移,即加上6—>7,6—>8,7—>9,8—>9这四条边,方案数就变为了6=110,再+1得到7=111的图,以此类推。

   所以,对于一个数k,我们从二进制的左边第一位开始向右找,每移动一位就加三个点,使方案数*2,若当前位上为1,则在下方加一条边从1连到当前的点,将方案数+1,否则不加,这样就构造出了整个图。

    因为要保证最短路,所以下方的路径要与上方对应,如图所示的加一条边的方法是非常节约点的,但本人的代码因为是在考场上写的,因此多加了很多无用的点,最极端的数据会用到899个点,但如果像上图一样加边的话,最多60*5个点就够了,代码如下:

#include<cstdio>#include<cstring>#include<iostream>#include<cmath>using namespace std;/*  101-->1011(加一条边)-->10110(*2) */int cnt,ll,last,k;//last表示加倍时应连接的最后一个点,cnt为点的总数int f[1000+10][1000+10];void expand()//方案数加倍{f[last][cnt+1]=f[last][cnt+2]=1;f[cnt+1][last]=f[cnt+2][last]=1;cnt+=3;f[cnt][cnt-1]=f[cnt][cnt-2]=1;f[cnt-1][cnt]=f[cnt-2][cnt]=1;ll+=2;last=cnt;}void add()//新加一条边{int ret=cnt;cnt+=ll;cnt--;int la=1;for(int j=ret+1;j<=cnt;j++){f[la][j]=f[j][la]=1;la=j;}f[la][last]=f[last][la]=1;}int lowbit(int x){  return x&(-x);}void work(){   int sum,tt=0;   int ret=k;   while(ret)   {   sum=lowbit(ret);    ret-=lowbit(ret);   }   //求第一个1   while(sum)   {   sum=(sum>>1);   tt++;   }   //初始化,有一条边,最后一个点是3   f[1][3]=1;   f[3][1]=1;   ll=1;   cnt=3;   last=3;      for(int i=tt-2;i>=0;i--)   {    expand();//左移一位    if((1<<i)&k)    {    add();//为1,加1条边    }   }   //最后一个点连上2   f[last][2]=1;   f[2][last]=1;   //输出   printf("%d\n",cnt);      for(int i=1;i<=cnt;i++)   {     for(int j=1;j<=cnt;j++)     {      if(f[i][j])      {         printf("Y");      }       else printf("N");     }     printf("\n");   }   }int main(){  scanf("%d",&k);    work();    return 0;}

   当然,Gemini同学用十进制也做出来了,还抢了一血,对其机智表示膜拜。


   下来后还看到了一份神代码,只用了100个点,还是为了凑整,简直膜拜,代码如下(注释是本人加的):

#include <stdio.h>#include <string.h>const int N = 105;int g[N][N];inline void set (int x, int y) {g[x][y] = g[y][x] = 1;}void init () //预处理出初始图 {memset(g, 0, sizeof(g));set(1, 3); set(1, 4); set(2, 100);for (int i = 3; i < 60; i += 2) //交叉加边 {set(i, i+2); set(i, i+3);set(i+1, i+2); set(i+1, i+3);}for (int i = 63; i < 100; i++) set(i, i+1);//直线连边 }void solve () {int k;scanf("%d", &k);for (int i = 30; i; i--) if (k >= (1<<i)) //二进制加边 {k -= (1<<i);set(2*i+1, 63+i); set(2*i+2, 63+i);}if (k) set(1, 63);}void put () //输出地图 {printf("100\n");for (int i = 1; i <= 100; i++) {for (int j = 1; j <= 100; j++)printf("%c", g[i][j] ? 'Y' : 'N');printf("\n");}}int main () {init();solve ();put();return 0;}
    代码很短,只有49行,运行速度超快。


    什么意思呢,大概如下图:

    他是先构建了一个这样的图,其中到3,4号点的路有1条,5,6有两条,61,62有2^29条路,这样的话,如果当前要加1的方案数,只需将1号点连到63号点,若要加2,则将3,4,一起连到64号点,以此类推。可以证明这些路的长度相等。因此,我们将k化为二进制,然后扫一遍,有1则加边,不然不管,就将图构建出来了。


    只能膜拜了。

      

0 0
原创粉丝点击