1. 程式人生 > >【hihocoder1257 2015北京賽區I】【構造 從公式入手】Snake Carpet 構造矩陣使得恰好容納1~n個拐拐蛇行圖案

【hihocoder1257 2015北京賽區I】【構造 從公式入手】Snake Carpet 構造矩陣使得恰好容納1~n個拐拐蛇行圖案

#include<stdio.h>
#include<string.h>
#include<map>
#include<vector>
#include<math.h>
#include<algorithm>
using namespace std;
#define MS(x,y) memset(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
const int dy[2][4]={0,1,0,1,  1,0,-1,0};
const int dx[2][4]={1,0,-1,0,  0,1,0,1};
int n,H,W;
const int N=1005;
vector<pair<int,int> >a[N];
int b[N][N];
void push(int w,int y,int x)
{
    a[w].push_back(MP(y,x));
    b[y][x]=w;
}
void listfill(int y,int x,int w1,int w2)
{
    int size1=w1;
    int size2=w2;
    int d=0;
    while(size1--)
    {
        push(w1,y,x);
        y+=dy[0][d];
        x+=dx[0][d];
        d=(d+1)%4;
    }
    while(size2--)
    {
        push(w2,y,x);
        y+=dy[0][d];
        x+=dx[0][d];
        d=(d+1)%4;
    }
}
void linefill(int y,int x,int w1,int w2)
{
    int size1=w1;
    int size2=w2;
    int d=0;
    while(size1--)
    {
        push(w1,y,x);
        y+=dy[1][d];
        x+=dx[1][d];
        d=(d+1)%4;
    }
    while(size2--)
    {
        push(w2,y,x);
        y+=dy[1][d];
        x+=dx[1][d];
        d=(d+1)%4;
    }
}
//用所以奇數畫正方形
void square(int y,int x,int top)
{
    for(int o=top;o>=1;o-=2)
    {
        ++y;
        ++x;
        int d=o/2;
        int yy=y+d;
        int xx=x+d;
        for(int i=yy;i>=y;i--)push(o,i,x);
        for(int i=x+1;i<=xx;i++)push(o,y,i);
    }
}
//用所有偶數畫矩形
void rectangle(int h,int w,int top)
{
	if(h&1)
	{
		int u=top;
		int v=h+h-u;
		int x=1;
		while(u>v)
		{
			listfill(1,x,u,v);
			u-=2;
			v+=2;
			x+=2;
		}
	}
	else
	{
		int u=top;
		int v=w+w-u;
		int y=1;
		while(u>v)
		{
			linefill(y,1,u,v);
			u-=2;
			v+=2;
			y+=2;
		}
	}
}
void odd()
{
    H=(n+1)/2;
	W=n;
    int mv=n;
	int h=H;
	int w=W-(mv+1)/2;
	square(0,w,mv);
	rectangle(h,w,n-1);
}
void even()
{
    H=n/2;
	W=n+1;
    int mv=n-1;
	int h=H;
	int w=W-(mv+1)/2;
	square(0,w,mv);
	rectangle(h,w,n);
}
void printmap()
{
    for(int i=1;i<=H;i++)
    {
        for(int j=1;j<=W;j++)printf("%d ",b[i][j]);
        puts("");
    }
}
void print()
{
    printf("%d %d\n",H,W);
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<i;j++)printf("%d %d ",a[i][j].first,a[i][j].second);
        puts("");
    }
    //printmap();
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)a[i].clear();
        if(n&1)odd();
        else even();
        print();
    }
    return 0;
}
/*
【trick&&吐槽】
比賽的時候不要太悠閒了,不給自己的壓力很多時候造成的惡果是,悲慘地只恰好差一分鐘AC TwT

【題意】
給你一個n([1,500]),表示有n條蛇,蛇的身長從1到n,每條蛇所佔的格子數也恰好等於其身長。
我們希望把這n條蛇,不交叉不溢位地都放到一個h*w的矩形中。(也就是說h*w=(1+n)*n/2)
並且使得:
對於身長為奇數的蛇(1除外),其扭曲的次數恰好為奇數;
對於身長為偶數的蛇(2除外),其扭曲的次數恰好為偶數。
讓你構造出其方案,並按照身長從1~n,依次輸出所有蛇。對於每條蛇,從頭到尾輸出其u偶在位置。

【型別】
構造

【分析】
首先,我們要分析這個矩形的形狀,矩形的形狀是h*w的話,h和w必定是其面積和的因子。
於是我每次想要枚舉出最小的滿足這個要求的h,然後再進行構造,然後就踏上了悲慘之路。
一個比較優秀的姿勢是什麼呢?
就是——既然h*w=(1+n)*n/2,我們就從這個公式入手!
所以,如果n為奇數,那我們就有因子{(1+n)/2和n};
	  如果n為偶數,那我們就有因子{n/2和(1+n)}。
然後,我們觀察到,不論n為奇數還是偶數,對於所有的奇數,我們都是可以這麼安排的——
1357
3357
5557
7777
我們發現,當n為奇數時,所有的奇數,恰好可以構成一個(n+1)/2的正方形
		  當n為偶數時,所有的奇數,恰好可以構成一個(n-1+1)/2=n/2的正方形
於是,我們可以剔除這個正方形。

然後,對於n為奇數,目前的矩形就是(n+1)/2 * (n-1)/2
	  對於n為偶數,目前的矩形就是n/2 * (n+2)/2

然後我們此時剩下的全部都是偶數
對於n%4==1,(n+1)/2 * (n-1)/2	可以用(n-1,2),(n-3,4)這樣湊,形成的對數恰好是(n-1)/4對,每對的和恰好是n+1
對於n%4==2,n/2 * (n+2)/2		可以用(n)(n-2,2),(n-4,4)這樣湊,形成的對數恰好是(n+2)/4對,每對的和恰好是n
對於n%4==3,(n+1)/2 * (n-1)/2	可以用(n-1)(n-3,2),(n-5,4)這樣湊,形成的對數恰好是(n+1)/4對,每對的和恰好是n-1
對於n%4==0,n/2 * (n+2)/2		可以用(n,2)(n-2,4),(n-4,6)這樣湊,形成的對數恰好是n/4對,每對的和恰好是(n+2)

而()中的每個數或每一對,能拐就拐,於是就都很容易就在2*d的格子中構造出解。於是就可以AC啦。
這個可以分開寫,也可以歸納寫,總之我們就可以這樣AC啦~啦啦啦啦~

【時間複雜度&&優化】
O((n+1)*n/2)

*/