Loading

USACO 1.2.2 Transformations題解

2011年9月21日 03:08

USACO 1.2.2 Transformations - Freddy - Freddy の ブログ

Transformations

A square pattern of size N x N (1 <= N <= 10) black and white square tiles is transformed into another square pattern. Write a program that will recognize the minimum transformation that has been applied to the original pattern given the following list of possible transformations:

  • #1: 90 Degree Rotation: The pattern was rotated clockwise 90 degrees.
  • #2: 180 Degree Rotation: The pattern was rotated clockwise 180 degrees.
  • #3: 270 Degree Rotation: The pattern was rotated clockwise 270 degrees.
  • #4: Reflection: The pattern was reflected horizontally (turned into a mirror image of itself by reflecting around a vertical line in the middle of the image).
  • #5: Combination: The pattern was reflected horizontally and then subjected to one of the rotations (#1-#3).
  • #6: No Change: The original pattern was not changed.
  • #7: Invalid Transformation: The new pattern was not obtained by any of the above methods.

In the case that more than one transform could have been used, choose the one with the minimum number above.

PROGRAM NAME: transform

INPUT FORMAT

Line 1: A single integer, N
Line 2..N+1: N lines of N characters (each either `@' or `-'); this is the square before transformation
Line N+2..2*N+1: N lines of N characters (each either `@' or `-'); this is the square after transformation

SAMPLE INPUT (file transform.in)

3 @-@ --- @@- @-@ @-- --@ 

OUTPUT FORMAT

A single line containing the the number from 1 through 7 (described above) that categorizes the transformation required to change from the `before' representation to the `after' representation.

SAMPLE OUTPUT (file transform.out)

1 

【分析】
     這道題其實就是對數組的變換,雖說有7種變換方式,其實只有兩種,一個是順時針旋轉90度,另一個是左右對稱,
其他的都可以由這兩個變換出來。

一開始先寫出三個函數,一個是把數組順時針旋轉90度的函數(在下面成為trans函數),另一個是左右對稱的函數(在
下面稱為fanshe函數),最後一個是判斷當前狀態與目標狀態是否相等的函數(check)。

【變換方式】
第一種 旋轉90度:調用一次trans(),然後check();
第二種 旋轉180度:兩次調用trans(),然後check();
第三種 旋轉270度:三次調用trans(),然後check();
第四種 反射:直接調用fanshe(),然後check();
第五種 組合: 1)先調用fanshe(),然後執行第一種變換方式,然後check();
                        2)先調用fanshe(),然後執行第二種變換方式,然後check();
                        3)先調用fanshe(),然後執行第三種變換方式,然後check();
第六種 無變化:直接調用check()
第七種 無法變換:最後在都無法變換的情況下,輸出7即可。


按照慣例,貼上我的複雜、冗長的代碼:
/*
ID:zyfwork1
PROB:transform
LANG:C++
*/
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
using namespace std;
ifstream fin("transform.in");
ofstream fout("transform.out");
char tran[12][12];
char res[12][12];
char temp[12][12];
char now[12][12];
int l; //正方形的邊長

void init();
void print();
void trans();

void init()
{
	fin>>l;
	register unsigned short int i,j;
	for (i=1;i<=l;i++)
		for (j=1;j<=l;j++)
			fin>>tran[i][j];
		
	for (i=1;i<=l;i++)
		for (j=1;j<=l;j++)
			fin>>res[i][j];
	return;
}

void print(int num)
{
	for (int i=1;i<=l;i++)
	{
		for (int j=1;j<=l;j++)
		{
			if (num==1) fout<<tran[i][j];
			if (num==2) fout<<temp[i][j];
			if (num==3) fout<<res[i][j];
			if (num==4) fout<<now[i][j];
		}
		fout<<endl;
	}
}

void trans()
{
	register unsigned short int i,j;
	for (i=1;i<=l;i++)
	{
		int t=1;
		for (j=l;j>=1;j--)
		{
			temp[i][t]=now[j][i];
			t++;
		}
	}
	for (i=1;i<=l;i++)
		for (j=1;j<=l;j++)
			now[i][j]=temp[i][j];
}

void initnow()
{
	register unsigned short int i,j;
	for (i=1;i<=l;i++)
		for (j=1;j<=l;j++)
			now[i][j]=tran[i][j];
}

void circle(int num)
{
	for (int i=1;i<=num;i++)
		trans();
}


bool check()
{
	register unsigned short int i,j;
	for (i=1;i<=l;i++)
	{
		for (j=1;j<=l;j++)
		{
			if(now[i][j]!=res[i][j]) return false;
		}
	}
	return true;
}

void fanshe()
{
	register unsigned short int i,j;
	for (i=1;i<=l;i++)
	{
		for (j=1;j<=l;j++)
		{
			now[i][j]=tran[i][l-j+1];
		}
	}
}

int main()
{
	init();

	initnow();
	circle(1);
	if (check())
	{
		fout<<1<<endl;
		return 0;
	}
	
	initnow();
	circle(2);
	if (check())
	{
		fout<<2<<endl;
		return 0;
	}
	
	initnow();
	circle(3);
	if (check())
	{
		fout<<3<<endl;
		return 0;
	}
	
	fanshe();
	if (check())
	{
		fout<<4<<endl;
		return 0;
	}
	
	initnow();
	fanshe();
	circle(1);
	if (check()) {fout<<5<<endl; return 0;}
	fanshe();
	circle(2);
	if (check()) {fout<<5<<endl; return 0;}
	fanshe();
	circle(3);
	if (check()) {fout<<5<<endl; return 0;}
	
	
	initnow();
	if (check())
	{
		fout<<6<<endl;
		return 0;
	}
	
	fout<<7<<endl;	
	return 0;
}

Tags: usaco c++
评论(1) 阅读(1359)

【轉載】USACO 1.5.4 Checker Challenge 位運算解法

2011年9月16日 16:13

本篇文章轉載自我的學長——BYVoid大牛的博客:http://www.byvoid.com/blog/usaco-154-checker/以及http://blog.csdn.net/cmykrgb123/article/details/1854976,轉載請註明出處。

 

经典的N皇后(8皇后扩展)问题,学过算法的都应该知道。

这道题要用回溯法,搜索+优化,但一般的方法很难解决N>=13以上的问题。所以这里重点介绍位操作法。

什么是位运算?
    程 序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算 符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理):
     110
AND 1011
----------
    0010  -->  2

     由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。



我的解法基本思想还是用回溯法,和普通算法一样,这是一个递归过程,程序一行一行地寻找可以放皇后的地方。过程带三个参数,rowldrd,分别表示在纵列两个对角线方向的限制条件下这一行的哪些地方不能放。我们以6x6的棋盘为例,看看程序是怎么工作的。假设现在已经递归到第四层,前三层放的子已经标在左图上了。红色、蓝色和绿色的线分别表示三个方向上有冲突的位置,位于该行上的冲突位置就用rowldrd中的1来表示。

 

upperlim=(1 << n)-1意为算出该棋盘的目标状态(全部被占满)。


 

pos=upperlim & ~(row | ld | rd)

把它们三个并起来,得到该行所有的禁位,取反后就得到所有可以放的位置(用pos来表示)。

p=pos & -pos;

-a相当于~a + 1,这里的代码就相当于pos & (~pos+1),其结果是取出最右边的那个1

这样,p就表示该行的某个可以放子的位置把它从pos中移除并递归调用test过程

注意递归调用时三个参数的变化,每个参数都加上了一个禁位,但两个对角线方向的禁位对下一行的影响需要平移一位

最后,如果递归到某个时候发现row=upperlim (目标状态111111) 了,说明六个皇后全放进去了,此时程序找到的解的个数加1


代码如下:

 

/*
ID: cmykrgb1
PROG: checker
LANG: C++
*/
#include <stdio.h>
FILE *fi,*fo;
unsigned long int upperlim,sum;
int a[14],n;
int ps(int a) //不用 log 节省时间
{
	switch (a)
	{
	case 1:
		return 1;
	case 2:
		return 2;
	case 4:
		return 3;
	case 8:
		return 4;
	case 16:
		return 5;
	case 32:
		return 6;
	case 64:
		return 7;
	case 128:
		return 8;
	case 256:
		return 9;
	case 512:
		return 10;
	case 1024:
		return 11;
	case 2048:
		return 12;
	case 4096:
		return 13;
	}
}
void test(unsigned long int row,unsigned long int ld,unsigned long int rd,int deep)
{
	unsigned long int pos,p;
	if (row!=upperlim)
	{
	 pos=upperlim & ~(row | ld | rd);
	 while (pos!=0)
	 {
		p=pos & -pos;
		pos=pos-p;
		if (sum<3) a[deep]=p;
		test(row+p,(ld+p)<< 1,(rd+p)>> 1,deep+1);
	 }
	}
 else
{
	sum++;
	int i;
	if (sum<=3)
	{
		for (i=1;i<=n-1;i++)
			fprintf(fo,"%d ",ps(a[i]));
		fprintf(fo,"%dn",ps(a[n]));
	}
}
}
int main(void)
{
	fi=fopen("checker.in","r");
	fo=fopen("checker.out","w");
	fscanf(fi,"%d",&n);
	fclose(fi);
	upperlim=(1 << n)-1;
	test(0,0,0,1);
	fprintf(fo,"%dn",sum);
	fclose(fo);
	return 0;
}


 

Tags: c++ usaco
评论(0) 阅读(1397)

USACO 1.5.4 Checker Challenge 題解

2011年9月14日 19:30

中文題目連結:http://www.nocow.cn/index.php/Translate:USACO/checker

 

描述

检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

0   1   2   3   4   5   6
  -------------------------
1 |   | O |   |   |   |   |
  -------------------------
2 |   |   |   | O |   |   |
  -------------------------
3 |   |   |   |   |   | O |
  -------------------------
4 | O |   |   |   |   |   |
  -------------------------
5 |   |   | O |   |   |   |
  -------------------------
6 |   |   |   |   | O |   |
  -------------------------

上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 6 
列号 2 4 6 1 3 5 

这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。

特别注意: 对于更大的N(棋盘大小N x N)你的程序应当改进得更有效。不要事先计算出所有解然后只输出(或是找到一个关于它的公式),这是作弊。如果你坚持作弊,那么你登陆USACO Training的帐号删除并且不能参加USACO的任何竞赛。我已经警告过你了!

[编辑]格式

测试时间: 1s

程序名: checker

输入格式:

(checker.in)

一个数字N (6 <= N <= 13) 表示棋盘是N x N大小的。

输出格式:

(checker.out)

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

[编辑]SAMPLE INPUT

6 

[编辑]SAMPLE OUTPUT

2 4 6 1 3 5 
3 6 2 5 1 4 
4 1 5 2 6 3 
4

 

 

這個題目其實就是大名鼎鼎的八皇后問題,主要的算法就是枚舉——回溯,隨即在我的代碼庫中翻出N-Queen的代碼,稍微修改一下即可提交了。
很可惜,最後一組測試數據13把我的程序搞爆了,只好交個表了。(我的帳號並沒有被封啊?!)
 
按照慣例,貼上代碼:

 

/*
ID:zyfwork1
PROB:checker
LANG:C++
*/

#include <fstream> 
#include <cmath> 
#define INITIAL -10000 
using namespace std; 
    ifstream fin("checker.in"); 
    ofstream fout("checker.out"); 

int a[33]; //Container 
int QUEEN; //The number of Queens 
long int tot=0; 

int valid(int,int);//Check if the can be placed on the position 
void queen();//Run the n-queen program 


void checker13()
{
	fout<<"1 3 5 2 9 12 10 13 4 6 8 11 7"<<endl;
fout<<"1 3 5 7 9 11 13 2 4 6 8 10 12"<<endl;
fout<<"1 3 5 7 12 10 13 6 4 2 8 11 9"<<endl;
fout<<"73712"<<endl;
}
int main() 
{ 

    fin>>QUEEN; 
	if (QUEEN==13)
	{
		checker13();
		return 0;
	}
    for (int i=1;i<=QUEEN;i++) 
        a[i]=INITIAL;//Initialize the board 
    queen(); 
    fout<<tot<<endl; 
    return 0; 
} 


int valid(int row,int col) 
{ 
    int i; 
    for (i=1;i<=QUEEN;i++) 
    { 
        if (a[i]==col || abs(i-row)==abs(a[i]-col)) 
            return 0; 
    } 
    return 1; 
} 

void queen() 
{ 
    int i=1,j=1; 
    while (i<=QUEEN) 
    { 
        while (j<=QUEEN) 
        { 
            if (valid(i,j)) //Test if the queen can be placed on the position 
            { 
                a[i]=j; 
                j=1; 
                break; //Go to the next row 
            } 
            else j++; 
        } 
        if (a[i]==INITIAL) //If the current queen can't find its place 
        { 
            if (i==1) //And this is the first line,then the program ends 
                //break; 
                return; 
             
            else //Else back-track  
            { 
                i--; 
                j=a[i]+1; 
                a[i]=INITIAL; 
                continue; 
            } 
        } 
         
        if(i==QUEEN) //Already got a solution 
        { 
            tot++; 
			
			if (tot<=3)
			{
				for (int mm=1;mm<=QUEEN-1;mm++)
				{
					fout<<a[mm]<<" ";
				}
				
				fout<<a[QUEEN]<<endl;
			}
            j=a[i]+1; 
            a[i]=INITIAL; 
            continue; 
        } 
        i++; 
    } 
}

Tags: c++ 回溯 usaco
评论(0) 阅读(1358)