ปริศนาของ Piet (Mondrian)


20

สำหรับข้อมูลเพิ่มเติมดูวิดีโอนี้และไปที่A276523สำหรับลำดับที่เกี่ยวข้อง

ปริศนา Mondrian (สำหรับจำนวนเต็มn) มีดังต่อไปนี้:

ใส่สี่เหลี่ยมที่ไม่สอดคล้องกันลงในn*nตารางสี่เหลี่ยม อะไรคือความแตกต่างที่เล็กที่สุดระหว่างสี่เหลี่ยมที่ใหญ่ที่สุดกับสี่เหลี่ยมเล็กที่สุด?

สำหรับ6ความแตกต่างที่ดีที่สุดสำหรับM(6)คือ5และสามารถแสดงให้เห็นเช่น:

 ___________
| |S|_______|
| | |   L   |
| |_|_______|
| |     |   |
| |_____|___|
|_|_________| (fig. I)

สี่เหลี่ยมผืนผ้าใหญ่ที่สุด (L) มีพื้นที่2 * 4 = 8และสี่เหลี่ยมเล็กที่สุด (S) 1 * 3 = 3มีพื้นที่ 8 - 3 = 5ดังนั้นความแตกต่างคือ

เก็บไว้ในใจว่าขณะนี้ยังไม่มีการแก้ปัญหาที่ดีที่สุดสำหรับการn > 44ได้รับพบว่า

งานของคุณคือการสร้างโปรแกรมที่สร้างตาราง Mondrian ที่มี (ที่ไม่เหมาะสม) nวิธีการแก้ปัญหาให้เป็นจำนวนเต็ม

คุณจะได้รับการทดสอบตัวเลขตั้งแต่ 100 ถึง 150 คะแนนของคุณสำหรับการทดสอบแต่ละครั้งจะเป็นความแตกต่างระหว่างสี่เหลี่ยมผืนผ้าที่มีขนาดใหญ่ที่สุดและเล็กที่สุด คะแนนรวมของคุณคือผลรวมของคะแนนของคุณสำหรับการทดสอบทั้งหมดตั้งแต่ 100 ถึง 150

คุณต้องแสดงผลลัพธ์ของคุณดังนี้:

{number}
{grid}

numberคะแนนอยู่ที่ไหน(ความแตกต่างระหว่างค่าที่มากที่สุดและน้อยที่สุด) และgridคือ:

  • สตริงที่มีหลายแถวหรือ
  • รายการสองมิติ

ตารางจะต้องแสดงให้เห็นอย่างชัดเจนว่าจุดเริ่มต้นและสิ้นสุดของสี่เหลี่ยมเป็นอย่างไร

กฎ:

  • โปรแกรมของคุณจะต้องพอดีกับคำตอบของคุณ
  • โปรแกรมของคุณจะต้องส่งออกค่าสำหรับหมายเลขใด ๆ ระหว่าง 100 และ 150 ภายใน 1 ชั่วโมงบนแล็ปท็อปที่ทันสมัย
  • โปรแกรมของคุณต้องสร้างโซลูชันเดียวกันสำหรับจำนวนเต็มnทุกครั้งที่มีการเรียกใช้โปรแกรม
  • คุณต้องระบุลิงก์ไปยังผลลัพธ์ของโซลูชั่นทั้ง 51 (โดยใช้ Pastebin, Github Gist ... อะไรจริง ๆ )
  • คุณต้องมีอย่างน้อยสองรูปสี่เหลี่ยมบนตารางสี่เหลี่ยมสำหรับการแก้ปัญหาของคุณ

1
OEIS A276523 โปรดทราบว่าขอบเขตบนที่ระบุไว้นั้นง่ายมากในการปรับปรุง
Peter Taylor

ฮ้า ฉันดูวิดีโอเดียวกันเมื่อสัปดาห์ที่แล้วและความคิดแรกของฉันคือพยายามทำโปรแกรมเพื่อแก้ปัญหา แม้ว่าฉันจะลืมมันไปหมดแล้ว
Carcigenicate

4
เราต้องการคำตอบ Piet อาจได้รับรางวัลสำหรับมัน ...
NoOneIsHere

คำตอบ:


11

Piet, 9625

(ในที่สุดก็ใช้งานได้!)

ผู้คนต้องการมันดังนั้นนี่คือ นี่เป็นวิธีแก้ปัญหาที่ไร้เดียงสาอย่างมาก (โดยพื้นฐานแล้วเหมือนกับขอบเขตบนบนหน้า OEIS): มันแบ่งแต่ละตารางออกเป็นสี่เหลี่ยมสองรูปเท่านั้น

สรุปสาระสำคัญนี้มีรายละเอียดในสองไฟล์:

  • เอาต์พุตของโปรแกรม (โดยใช้ npiet v1.3) สำหรับอินพุตที่ต้องการทั้งหมด โปรดทราบว่าฉันจับ stdout เท่านั้นดังนั้นจึง?เป็นพรอมต์อินพุตตามด้วยคะแนนเอาต์พุตจากนั้นกริด
  • แหล่ง "ประกอบหลอก" ที่ฉันใช้ในการวางแผนโปรแกรม

Piet solution, codel size 10

คำอธิบาย

โปรแกรมนี้ใช้หมายเลขเดียวNเป็นอินพุต หากตัวเลขเป็นเลขคี่คะแนนจะเป็นจำนวน ถ้าเป็นเช่นนั้นคะแนนจะเป็นสองเท่าของจำนวน

หลังจากแสดงคะแนนแล้วส่วนที่เหลือทางด้านซ้ายของโปรแกรมจะถูกใช้จนเต็มด้วยข้อมูลต่อไปนี้จำนวนห้าชุด:

  • ความกว้างกริด (ซึ่งเป็นN)
  • จำนวนบรรทัดที่จะพิมพ์
  • อักขระที่พิมพ์ข้ามกริด ( _หรือเว้นวรรค)
  • อักขระที่พิมพ์ที่ขอบแต่ละด้านของกริด (เว้นวรรคหรือ|)

ทางด้านขวาของโปรแกรมใช้ค่าสี่ชุดแต่ละค่าแล้วพิมพ์ส่วนของกริดนั้น


คุณจะได้รับรางวัลต่อไป!
NoOneIsHere

การแก้ไขจะต้องถูกต้องที่จะโพสต์
mbomb007

@ mbomb007 ตกลงฉันไม่ทราบว่า ฉันหวังว่ามันจะเสร็จสมบูรณ์ใน 7 วัน
NoOneIsHere

6

C 6108

ใช้วิธีแก้ปัญหาขั้นต่ำรุ่น recursive (ซ้ำจริงๆ) แทนที่จะแบ่งสี่เหลี่ยมออกเป็นสองสี่เหลี่ยมโดยที่หนึ่งใหญ่กว่าพื้นที่ครึ่งหนึ่งนิดหน่อยมันจะแบ่งมันออกเป็นสี่เหลี่ยม N ดังนั้นสี่เหลี่ยมผืนผ้าแรกนั้นใหญ่กว่า1/Nพื้นที่ทั้งหมดเล็กน้อย จากนั้นนำส่วนที่เหลือออกโปรแกรมจะแยกสี่เหลี่ยมที่มีขนาดใหญ่กว่า1/(N-1)ของที่เหลือออกไปเรื่อย ๆ จนกระทั่งมันใช้เวลาที่เหลือเป็นสี่เหลี่ยมสุดท้าย รูปสี่เหลี่ยมผืนผ้าจะถูกตัดออกจากส่วนที่เหลือในเกลียวตามเข็มนาฬิกาดังนั้นก่อนที่ด้านบนแล้วด้านขวา ฯลฯ

เนื่องจากนี่เป็นวิธีที่ตรงมากแทนที่จะค้นหาพื้นที่กว้างมันจึงวิ่งเร็ว - ใช้เวลาประมาณ 25 วินาที (บน Raspberry Pi) เพื่อดูวิธีแก้ปัญหา 74 ชุดสำหรับชุดปัญหาที่กำหนด

ความตั้งใจของฉันคือการใช้ผลลัพธ์เหล่านี้เพื่อแจ้งอัลกอริทึมการค้นหาสำหรับวิธีการที่ซับซ้อนยิ่งขึ้น

ผลลัพธ์ให้คะแนนรวมทั้งการวาด (ascii) และพิกัดสำหรับจุดยอดของรูปสี่เหลี่ยม จุดยอดอยู่ในลำดับตามเข็มนาฬิกาเริ่มต้นจากมุมซ้ายบนของสี่เหลี่ยมที่มีปัญหา

พัฒนาโดยใช้ gcc 4.9.2-10

ผลลัพธ์ที่https://github.com/JaySpencerAnderson/mondrian

รหัส:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct {
    int y, x, height, width;
} rectangle;
#define min(x,y) (((x)<(y))?(x):(y))
#define max(x,y) (((x)>(y))?(x):(y))
#ifndef TRUE
#define TRUE -1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define MAXCOUNT 75

void initstack(rectangle *s, int n){
    int i;
    for(i=0;i<n;i++){
        s[i].y=s[i].x=s[i].height=s[i].width=0;
    }
}
int valid(rectangle *s,int n){
    int i,j;
    for(i=0;i<n-1;i++){
        for(j=i+1;j<n;j++){
            if(min(s[i].height,s[i].width) == min(s[j].height,s[j].width) && max(s[i].height,s[i].width) == max(s[j].height,s[j].width)){

                initstack(s, n);
                return FALSE;
            }
        }
    }
    return TRUE;
}
int horizontal(rectangle s, int y, int x){
    if(s.y == y && x >= s.x && x < s.x+s.width){
        return TRUE;
    }
    else if(s.y+s.height == y && x >= s.x && x < s.x+s.width){
        return TRUE;
    }
    return FALSE;
}
int vertical(rectangle s, int y, int x){
    if(s.x == x && y > s.y && y <= s.y+s.height){
        return TRUE;
    }
    else if(s.x+s.width == x && y > s.y && y <= s.y+s.height){
        return TRUE;
    }
    return FALSE;
}
void graph(rectangle *s, int n, int side){
    unsigned int row,col,i;
    unsigned int line;
    printf("{\n");
/* vertical lines take precedence since "1" cell is 1 char high and 2 char wide */
    for(row=0;row<=side;row++){
        for(col=0;col<=side;col++){
            line=0;
/* Possible values are "  " (0), "__" (1), "| " (2) or "|_" (3). */
            for(i=0;i<n;i++){
                if(horizontal(s[i],row,col)){
                    line|=1;
                }
                if(vertical(s[i],row,col)){
                    line|=2;
                }
            }

            switch(line){
            case 0: printf("  ");   break;
            case 1: printf("__");   break;
            case 2: printf("| ");   break;
            case 3: printf("|_");   break;
            default: printf("##");  break;
            }
        }
        printf("\n");
    }
    printf("}\n");
}
unsigned int score(rectangle *s, int n){
    int i;
    unsigned int smallest,biggest;

    smallest=biggest=s[0].width*s[0].height;

    for(i=0;i<n;i++){
        smallest=min(smallest,s[i].width*s[i].height);
        biggest=max(biggest,s[i].width*s[i].height);
    }
    return biggest-smallest;
}
void report(rectangle *s, int n, int side){
    int i;

    printf("{%d}\n",score(s,n));
    graph(s, n, side);
    printf("{\n");
    for(i=0;i<n;i++){
        printf("[%d,%d] ",s[i].x,s[i].y);
        printf("[%d,%d] ",s[i].x+s[i].width,s[i].y);
        printf("[%d,%d] ",s[i].x+s[i].width,s[i].y+s[i].height);
        printf("[%d,%d]\n",s[i].x,s[i].y+s[i].height);
    }
    printf("\n}\n");
}
void locateandrotate(rectangle *stack, int n){
    unsigned int scratch,i;
    for(i=1;i<n;i++){
        /* Odd rectangles are on their side */
        if(i&1){
            scratch=stack[i].width;
            stack[i].width=stack[i].height;
            stack[i].height=scratch;
        }
        switch(i%4){
        case 0:
            stack[i].x=stack[i-1].x+stack[i-1].width;
            stack[i].y=stack[i-1].y;
            break;
        case 1:
            stack[i].x=stack[i-1].x+stack[i-1].width-stack[i].width;
            stack[i].y=stack[i-1].y+stack[i-1].height;
            break;
        case 2:
            stack[i].x=stack[i-1].x-stack[i].width;
            stack[i].y=stack[i-1].y+stack[i-1].height-stack[i].height;
            break;
        case 3:
            stack[i].x=stack[i-1].x;
            stack[i].y=stack[i-1].y-stack[i].height;
            break;
        default:
            printf("Woops!\n");
        }
    }
}
/* These are the height and width of the remaining area to be filled. */
void door(rectangle *stack, unsigned int height, unsigned int width, unsigned int n, unsigned int totaln){
    unsigned int thisheight, thiswidth;
    int i;

    for(i=0;i<totaln;i++){
/* Not yet used */
        if(stack[i].width == 0){
            stack[i].width=width;
            if(i+1 == totaln){
                stack[i].height=height;
            }
            else {
/* Sometimes yields congruent rectangles, as with 16x16, 8 rectangles */
                if(totaln&1 || height%n){
                    int j;
                    stack[i].height=height-(((n-1)*height)/n);
                }
                else {
                    stack[i].height=height-((((n-1)*height)-1)/n);
                }
                /* Exchange height and width to rotate */
                door(stack,width,height-stack[i].height,n-1,totaln);
            }
            return;
        }
    }
}
void usage(char *argv[],int side){
    printf("Usage: %s -s <side-length>\n",argv[0]);
    printf("Purpose: Calculate N non-congruent rectangles arranged to exactly fill a square with the specified side length.\n");
    printf("Defaults: %s -s %d\n",argv[0],side);
    exit(0);

}
int main(int argc, char *argv[]){
    int side=16;
    int n,bestscore,bestn=2;
    int status;

    while((status=getopt(argc,argv,"s:h")) >= 0){
        switch(status){
        case 's':
            sscanf(optarg,"%d",&side);
            break;
        case 'h':
        default:
            usage(argv,side);
        }
    }

    bestscore=side+side;

    rectangle stack[MAXCOUNT],best[MAXCOUNT];
    for(n=2;n<=MAXCOUNT;n++){
        initstack(stack,MAXCOUNT);
        door(stack, side, side, n, n);
        locateandrotate(stack, n);
        if(valid(stack,n)){
            if(score(stack,n) < bestscore){
                bestn=n;
                initstack(best,MAXCOUNT);
                door(best, side, side, n, n);
                locateandrotate(best, n);

                bestscore=score(best,n);
            }
        }
    }
    report(best,bestn,side);
}

1
อืมมมม ... คุณสามารถให้คะแนนสุดท้ายในส่วนหัวได้หรือไม่? ขอบคุณ โซลูชันที่ดีแม้ว่า - ไม่ได้คาดหวังโซลูชัน (เพราะไม่มีใครตอบคำถามนี้สองสามวัน)
clismique

1

C - 2982

โปรแกรมนี้ใช้การค้นหาผ่านชุดผลลัพธ์แบบกว้าง ส่วนที่สำคัญในการทำให้การค้นหาในทางปฏิบัตินี้เกิดขึ้นได้ก็คือการล้มเหลว แต่เนิ่นๆและ / หรือไม่ลงเส้นทางที่ไม่ดี

สิ่งนี้จะสร้างชุดของรูปสี่เหลี่ยมผืนผ้าที่จะพิจารณาสำหรับการแก้ปัญหา ชุดของรูปสี่เหลี่ยมผืนผ้าที่สร้างขึ้นจะหลีกเลี่ยงสิ่งที่มีขนาดซึ่งจะไม่มีประโยชน์ ตัวอย่างเช่นหากโปรแกรมพยายามค้นหาวิธีแก้ปัญหาเป็นสี่เหลี่ยมจัตุรัส 128x128 แบ่งออกเป็น 8 สี่เหลี่ยมมันจะสร้างสี่เหลี่ยมที่มีขนาด 128x16 มันจะไม่สร้างอันนั้นคือ 120x17 เนื่องจากไม่มีโอกาสของสี่เหลี่ยมสร้างที่มีความกว้าง 8 เพื่อเติมเต็มช่องว่างในตอนท้ายของ 120

กลยุทธ์เริ่มต้นสำหรับการวางรูปสี่เหลี่ยมคือการวางไว้ที่ด้านในของเส้นรอบวงของตาราง (ฟังก์ชัน buildedge) ด้วยวิธีนี้อัลกอริทึมจะได้รับการตอบรับอย่างรวดเร็วในแต่ละมุมว่ามีปัญหากับลำดับที่เลือกหรือไม่ ในขณะที่วางรูปสี่เหลี่ยมตรรกะจะคอยดูว่าช่องว่างใด ๆ ของการพัฒนาที่แคบเกินไปสำหรับสี่เหลี่ยมใด ๆ หลังจากการเติมขอบเขตให้สำเร็จกลยุทธ์จะเปลี่ยนเป็นพยายามจับคู่พื้นที่ที่เหลือกับสี่เหลี่ยมที่เหลือ (ฟังก์ชันจับคู่)

อีกสิ่งหนึ่งที่อาจเป็นที่น่าสนใจก็คือการทำธุรกรรมนี้มีการย้อนกลับสำหรับกองสี่เหลี่ยม

โปรแกรมนี้ไม่ได้พยายามที่จะหาแบบที่ดีที่สุด จะได้รับงบประมาณ (64) และออกเมื่อพบโซลูชั่นแรก หากไม่พบวิธีแก้ปัญหาเราชนงบประมาณ (เพิ่มขึ้น 16) และลองอีกครั้ง เวลาที่ต้องการ (บนแล็ปท็อป Dell ที่มีโปรเซสเซอร์ I7) อยู่ในช่วงเวลาไม่เกิน 48 นาทีเป็น 150 นาทีสำหรับด้านข้าง (ด้านข้าง 149 หน่วยใช้เวลาน้อยกว่า 2 นาที) โซลูชันทั้งหมด 51 รายการใช้ 11 สี่เหลี่ยม คะแนนของ 51 โซลูชั่นอยู่ในช่วง 41 ถึง 78 เหตุผลที่ฉันใช้ 11 รูปสี่เหลี่ยมก็คือคะแนนนั้นต่ำกว่าเมื่อมีรูปสี่เหลี่ยมผืนผ้าน้อยกว่าและดูเหมือนว่ารูปสี่เหลี่ยม 12 รูปนั้นจะใช้เวลามากกว่าชั่วโมงที่กำหนด

โซลูชั่นและรหัสอาจจะพบว่าhttps://github.com/JaySpencerAnderson/mondrian เป็นไฟล์ my4 * สองไฟล์

BTW ถ้าคุณคอมไพล์นี่เป็น "my4" และดำเนินการดังนี้: "./my4 -h" มันจะให้การใช้งานแก่คุณ หากคุณต้องการที่จะเห็นมันในการทำงานออกไปลองบางอย่างเช่น "./my4 -l 50 -n 8" หากคุณเปลี่ยน "#if 0" เป็น "#if 1" จะเป็นการแสดงพื้นที่ที่เหลือบนหน้าจอ หากคุณต้องการเปลี่ยนสิ่งนี้เพื่อแสดงผลเป็นรูปสี่เหลี่ยมผืนผ้าให้มองหาจุดเดียวที่รหัสเรียกใช้ "กราฟ (ช่องว่างด้านข้าง)" และเปลี่ยนเป็น "กราฟ (โทรกลับด้านข้าง)" แทน ฉันขอแนะนำให้เปลี่ยนงบประมาณเริ่มต้นจาก 64 เป็น 32 หากคุณต้องการแก้ไขด้วยวิธีแก้ปัญหาสำหรับช่องสี่เหลี่ยมที่มีความกว้างประมาณ 50 วิธีแก้ปัญหาของกำลังสองที่น้อยกว่าจะได้คะแนนที่ดีกว่าด้วยงบประมาณที่น้อยลง

โปรแกรมด้านล่างใช้งานได้ ตรวจสอบ GitHub สำหรับรหัสที่สมบูรณ์ (พร้อมการใช้งานความคิดเห็นและอื่น ๆ )

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct {
    int y, x, height, width, created, deleted;
} rectangle;
#define NOTYET -1
#define TOPEDGE 1
#define RIGHTEDGE 2
#define BOTTOMEDGE 4
#define LEFTEDGE 8
#define CENTER 16
#define nextEdge(e) (e<<=1)
#define min(x,y) (((x)<(y))?(x):(y))
#define max(x,y) (((x)>(y))?(x):(y))
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define MAXFACTORS 1000
#define EOL printf("\n")
#define isCurrent(r) (r.created != NOTYET && r.deleted == NOTYET)
#define deleteTxn(r,t) (r.deleted=t)
int area(rectangle r){
    return r.width*r.height;
}
void pop(rectangle *s){
    unsigned int k=0;
    while(s[k].width){
        k++;
    }
    s[k-1].width=s[k-1].height=0;
}
void rpush(rectangle *s, rectangle x){
    unsigned int k=0;
    while(s[k].width){
        k++;
    }
    x.deleted=NOTYET;
    s[k++]=x;
    s[k].width=s[k].height=0;

    return;
}
void dumprectangle(rectangle r){
    printf("%dX%d@[%d,%d] (%d,%d)\t",r.width, r.height, r.x, r.y, r.created, r.deleted);
}
void dumpstack(rectangle *s){
    unsigned int k=0;
    while(s[k].width){
        dumprectangle(s[k]);
        k++;
    }
}
rectangle initrectangle(int width, int height){
    rectangle r;
    r.x=r.y=0;
    r.width=width;
    r.height=height;
    r.created=0;
    r.deleted=NOTYET;
    return r;
}
void initstack(rectangle *s, int n){
    int i;
    for(i=0;i<n;i++){
        s[i].y=s[i].x=s[i].height=s[i].width=0;
    }
}
int bitcount(int x){
    int count=0;
    while(x){
        if(x&1){
            count++;
        }
        x>>=1;
    }
    return count;
}
int congruent(rectangle a, rectangle b){
    return min(a.height,a.width) == min(b.height,b.width) && max(a.height,a.width) == max(b.height,b.width);
}
void report(rectangle *s, int side){
    int i;
    unsigned int smallest,biggest,area=0;

    smallest=side*side;
    biggest=0;

    for(i=0;s[i].width;i++){
        if(isCurrent(s[i])){
            smallest=min(smallest,s[i].width*s[i].height);
            biggest=max(biggest,s[i].width*s[i].height);
        }
    }
    printf("{%d}\n",biggest-smallest);
    printf("{\nDimensions\tLocation\n");
    for(i=0;s[i].width;i++){
        printf("%dx%d\t\t[%d,%d]\n",
            s[i].width,         s[i].height,
            s[i].x,             s[i].y);
    }
    printf("}\n");
}
unsigned int sumstack(rectangle *s){
    unsigned int sum=0;
    int i;
    for(i=0;s[i].width;i++){
        if(isCurrent(s[i])){
            sum+=s[i].width*s[i].height;
            s++;
        }
    }
    return sum;
}
unsigned int minstack(rectangle *s){
    unsigned int area=400000;
    int i;

    for(i=0;s[i].width;i++){
        if(isCurrent(s[i])){
            area=min(area,s[i].width*s[i].height);
        }
    }
    return area;
}
void rollback(rectangle *r, int txn){
    int i;

    if(txn != NOTYET){
        for(i=0;r[i].width;i++){
            if(r[i].created == txn){
                r[i].created=r[i].deleted=NOTYET;
                r[i].x=r[i].width=r[i].y=r[i].height=0;
            }
            else if(r[i].deleted == txn){
                r[i].deleted=NOTYET;
            }
        }
    }
}
int overlap(rectangle a, rectangle b){
    if((a.x < b.x+b.width && a.x+a.width > b.x) && (b.y < a.y+a.height && b.y+b.height > a.y)){
        return TRUE;
    }
    return FALSE;
}
int stackoverlap(rectangle *callstack, rectangle next){
    int i,j;
    for(i=0;callstack[i].width;i++){
        if(overlap(callstack[i], next)){
            return TRUE;
        }
    }
    return FALSE;
}
rectangle rotate(rectangle a){
    int x=a.width;
    a.width=a.height;
    a.height=x;
    return a;
}
int buildedge(rectangle *stack, rectangle *callstack,int side, rectangle *space){
    int i,j,edge,goal,nextgoal,x,y,d,mindim,minarea,result=FALSE,spacetxn,stacktxn;
    mindim=side;
    minarea=side*side;
    for(i=0;stack[i].width;i++){
        mindim=min(mindim,min(stack[i].width,stack[i].height));
        minarea=min(minarea,area(stack[i]));
    }
    x=y=0;
    edge=TOPEDGE;
    i=0;
    while(edge == TOPEDGE && callstack[i].width != 0){
        if(callstack[i].x == x && callstack[i].y == y){
            x+=callstack[i].width;
            if(x == side){
                nextEdge(edge);
                y=0;
            }
            i=0;
        }
        else {
            i++;
        }
    }
    while(edge == RIGHTEDGE && callstack[i].width != 0){
        if(callstack[i].x+callstack[i].width == x && callstack[i].y == y){
            y+=callstack[i].height;
            if(y == side){
                nextEdge(edge);
                x=side;
            }
            i=0;
        }
        else {
            i++;
        }
    }
    while(edge == BOTTOMEDGE && callstack[i].width != 0){
        if(callstack[i].x+callstack[i].width == x && callstack[i].y+callstack[i].height == y){
            x-=callstack[i].width;
            if(x == 0){
                nextEdge(edge);
                y=side;
            }
            i=0;
        }
        else {
            i++;
        }
    }
    while(edge == LEFTEDGE && callstack[i].width != 0){
        if(callstack[i].x == x && callstack[i].y+callstack[i].height == y){
            y-=callstack[i].height;
            if(y == 0){
                nextEdge(edge);
            }
            i=0;
        }
        else {
            i++;
        }
    }
    if(edge == CENTER){
        /* rectangles are placed all along the perimeter of the square.
         * Now match will use a different strategy to match the remaining space
         * with what remains in stack */
        if(match(stack,callstack,space)){
            report(callstack,side);
            return TRUE;
        }
        return FALSE;
    }
    switch(edge){
    case TOPEDGE:
        goal=side-x;
        break;
    case RIGHTEDGE:
        goal=side-y;
        break;
    case BOTTOMEDGE:
        goal=x;
        break;
    case LEFTEDGE:
        /* Still a good assumption that callstack[0] is at 0,0 */
        goal=y-callstack[0].height;
        break;
    default:
        fprintf(stderr,"Error: buildedge has unexpected edge (b): %d\n",edge);
        exit(0);
    }
    nextgoal=goal-mindim;
    for(i=0;stack[i].width;i++){
        if(isCurrent(stack[i])){
            for(d=0;d<2;d++){
                switch(edge){
                case TOPEDGE:
                    if(stack[i].width == goal || stack[i].width <= nextgoal){
                        stack[i].x=x;
                        stack[i].y=y;
                        if(!stackoverlap(callstack, stack[i])){
                            spacetxn=nexttransaction(space);
                            stacktxn=nexttransaction(stack);
                            deleteTxn(stack[i],stacktxn);
                            removerectangle(space, stack[i], spacetxn);
                            if(narrow(space) >= mindim && smallest(space) >= minarea){
                                rpush(callstack, stack[i]);
                                if(buildedge(stack, callstack, side, space)){
                                    return TRUE;
                                }
                                pop(callstack);
                            }
                            rollback(space, spacetxn);
                            rollback(stack, stacktxn);
                            stack[i].x=stack[i].y=0;
                        }
                    }
                    break;
                case RIGHTEDGE:
                    if(stack[i].height == goal || stack[i].height <= nextgoal){
                        stack[i].x=x-stack[i].width;
                        stack[i].y=y;
                        if(!stackoverlap(callstack, stack[i])){
                            spacetxn=nexttransaction(space);
                            stacktxn=nexttransaction(stack);
                            deleteTxn(stack[i],stacktxn);
                            removerectangle(space, stack[i], spacetxn);
                            if(narrow(space) >= mindim && smallest(space) >= minarea){
                                rpush(callstack, stack[i]);
                                if(buildedge(stack, callstack, side, space)){
                                    return TRUE;
                                }
                                pop(callstack);
                            }
                            rollback(space, spacetxn);
                            rollback(stack, stacktxn);
                            stack[i].x=stack[i].y=0;
                        }
                    }
                    break;
                case BOTTOMEDGE:
                    if(stack[i].width == goal || stack[i].width <= nextgoal){
                        stack[i].x=x-stack[i].width;
                        stack[i].y=y-stack[i].height;
                        if(!stackoverlap(callstack, stack[i])){
                            spacetxn=nexttransaction(space);
                            stacktxn=nexttransaction(stack);
                            deleteTxn(stack[i],stacktxn);
                            removerectangle(space, stack[i], spacetxn);
                            if(narrow(space) >= mindim && smallest(space) >= minarea){
                                rpush(callstack, stack[i]);
                                if(buildedge(stack, callstack, side, space)){
                                    return TRUE;
                                }
                                pop(callstack);
                            }
                            rollback(space, spacetxn);
                            rollback(stack, stacktxn);
                            stack[i].x=stack[i].y=0;
                        }
                    }
                    break;
                case LEFTEDGE:
                    if(stack[i].height == goal || stack[i].height <= nextgoal){
                        stack[i].x=x;
                        stack[i].y=y-stack[i].height;
                        if(!stackoverlap(callstack, stack[i])){
                            spacetxn=nexttransaction(space);
                            stacktxn=nexttransaction(stack);
                            deleteTxn(stack[i],stacktxn);
                            removerectangle(space, stack[i], spacetxn);
                            if(narrow(space) >= mindim && smallest(space) >= minarea){
                                rpush(callstack, stack[i]);
                                if(buildedge(stack, callstack, side, space)){
                                    return TRUE;
                                }
                                pop(callstack);
                            }
                            rollback(space, spacetxn);
                            rollback(stack, stacktxn);
                            stack[i].x=stack[i].y=0;
                        }
                    }
                    break;
                default:
                    fprintf(stderr,"Error: buildedge has unexpected edge (c): %d\n",edge);
                    exit(0);
                }
                if(callstack[0].width != 0 && stack[i].width != stack[i].height){
                    stack[i]=rotate(stack[i]);
                }
                else {
                    break;
                }
            }
        }
    }
    return FALSE;
}
int populatestack(rectangle *stack, int score, int side, int rectangles){
    int offset,negative,area,mindim;
    rectangle local;

    int avg_area=(side*side)/rectangles;

    if(avg_area < 4){
        /* It's getting too small - really */
        return FALSE;
    }
    local.x=0;
    local.y=0;
    local.created=0;
    local.deleted=NOTYET;

    initstack(stack,MAXFACTORS);
    for(offset=1;offset<=score;offset++){
        negative=offset&1;
        area=avg_area + (negative?(0-(offset>>1)):(offset>>1));
        mindim=area/side;

        if(side*(area/side) == area){
            local.width=side;
            local.height=area/side;
            rpush(stack,local);
        }

        if(area > 0){
            for(local.width=side-mindim;local.width>=area/local.width;local.width--){
                if(local.width*(area/local.width) == area){
                    local.height=area/local.width;
                    rpush(stack,local);
                }
            }
        }
    }
    return TRUE;
}
int solve(int side,int rectangles,int score){
    rectangle stack[MAXFACTORS],callstack[MAXFACTORS];
    rectangle space[MAXFACTORS];
    rectangle universe;

    if(!populatestack(stack, score, side, rectangles)){
        return FALSE;
    }
    if(sumstack(stack) >= side*side){
        initstack(callstack,MAXFACTORS);
        initstack(space,MAXFACTORS);

        /* Initialize space (not occupied by a rectangle) to be side by side
         * where side is the height/width of the square into which the rectangles fit. */
        universe.width=universe.height=side;
        universe.x=universe.y=0;
        universe.created=0;
        universe.deleted=NOTYET;
        rpush(space, universe);

        if(buildedge(stack,callstack,side,space)){
            return TRUE;
        }
    }
    return FALSE;
}
int containsPoint(rectangle a, int x, int y){
    return a.x <= x && a.y <= y && a.x+a.width > x && a.y+a.height > y;
}
int containsRectangle(rectangle a, rectangle b){
    return containsPoint(a, b.x, b.y) && containsPoint(a, b.x+b.width-1, b.y) && containsPoint(a, b.x, b.y+b.height-1) && containsPoint(a, b.x+b.width-1, b.y+b.height-1);
}
int areEqual(rectangle a, rectangle b){
    return a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height;
}
int nexttransaction(rectangle *r){
    int i,n=NOTYET;

    for(i=0;r[i].width;i++){
        n=max(n,max(r[i].created,r[i].deleted));
    }
    return n+1;
}
void splitrectanglevertically(rectangle *space, int i, int x, int txn){
    rectangle left, right;
    left=right=space[i];
    right.x=x;
    left.width=right.x-left.x;
    right.width-=left.width;
    left.created=right.created=space[i].deleted=txn;
    rpush(space,left);
    rpush(space,right);
}
void splitrectanglehorizontally(rectangle *space, int i, int y, int txn){
    rectangle top, bottom;
    top=bottom=space[i];
    bottom.y=y;
    top.height=bottom.y-top.y;
    bottom.height-=top.height;
    top.created=bottom.created=space[i].deleted=txn;
    rpush(space,top);
    rpush(space,bottom);
}
int smallest(rectangle *space){
    int i,j,smallest;
    rectangle current;
    smallest=0;
    for(i=0;space[i].width;i++){
        if(isCurrent(space[i])){
            current=space[i];
            for(j=0;space[j].width;j++){
                if(isCurrent(space[j]) && i != j){
                    if(current.x+current.width == space[j].x
                    && space[j].y <= current.y && space[j].y+space[j].height >= current.y+current.height){
                        current.width+=space[j].width;
                    }
                    else if(space[j].x+space[j].width == current.x
                    && space[j].y <= current.y && space[j].y+space[j].height >= current.y+current.height){
                        current.x=space[j].x;
                        current.width+=space[j].width;
                    }
                    else if(current.y+current.height == space[j].y
                    && space[j].x <= current.x && space[j].x+space[j].width >= current.x+current.width){
                        current.height+=space[j].height;
                    }
                    else if(space[j].y+space[j].height == current.y
                    && space[j].x <= current.x && space[j].x+space[j].width >= current.x+current.width){
                        current.y=space[j].y;
                        current.height+=space[j].height;
                    }
                }
            }
            if(smallest == 0){
                smallest=current.width * current.height;
            }
            else if(smallest > current.width * current.height){
                smallest=current.width * current.height;
            }
        }
    }
    return smallest;
}
int narrow(rectangle *space){
    int i,j;
    rectangle smallest,current;

    smallest.width=0;
    for(i=0;space[i].width;i++){
        current=space[i];
        if(isCurrent(current)){
            for(j=0;space[j].width;j++){
                if(isCurrent(space[j]) && i != j){
                    if(current.width <= current.height
                    && current.x+current.width == space[j].x
                    && space[j].y <= current.y && space[j].y+space[j].height >= current.y+current.height){
                        current.width+=space[j].width;
                    }
                    else if(current.width <= current.height
                    && space[j].x+space[j].width == current.x
                    && space[j].y <= current.y && space[j].y+space[j].height >= current.y+current.height){
                        current.x=space[j].x;
                        current.width+=space[j].width;
                    }

                    if(current.width >= current.height
                    && current.y+current.height == space[j].y
                    && space[j].x <= current.x && space[j].x+space[j].width >= current.x+current.width){
                        current.height+=space[j].height;
                    }
                    else if(current.width >= current.height
                    && space[j].y+space[j].height == current.y
                    && space[j].x <= current.x && space[j].x+space[j].width >= current.x+current.width){
                        current.y=space[j].y;
                        current.height+=space[j].height;
                    }
                }
            }
            if(smallest.width == 0){
                smallest=current;
            }
            else if(min(smallest.width,smallest.height) > min(current.width,current.height)){
                smallest=current;
            }
        }
    }
    return min(smallest.width,smallest.height);
}
int notEmpty(rectangle *space){
    int i,count;

    for(i=0,count=0;space[i].width;i++){
        if(isCurrent(space[i])){
            count++;
        }
    }
    return count;
}
int isAdjacent(rectangle r, rectangle s){
    if(r.y == s.y+s.height && r.x < s.x+s.width && s.x < r.x+r.width){
        return TOPEDGE;
    }
    if(s.x == r.x+r.width && r.y < s.y+s.height && s.y < r.y+r.height){
        return RIGHTEDGE;
    }
    if(s.y == r.y+r.height && r.x < s.x+s.width && s.x < r.x+r.width){
        return BOTTOMEDGE;
    }
    if(r.x == s.x+s.width && r.y < s.y+s.height && s.y < r.y+r.height){
        return LEFTEDGE;
    }
    return NOTYET;
}

int adjacentrectangle(rectangle *space, int k, int k0){
    int i,edge;
    for(i=k0+1;space[i].width;i++){
        if(i != k && isCurrent(space[i])){
            if(isAdjacent(space[k],space[i]) != NOTYET){
                return i;
            }
        }
    }
    return NOTYET;
}
int expanse(rectangle *space, int j, int d){ /* Returns how far space[j] can expand in the d direction */
    int extent,k,giveUp,distance;
    rectangle result=space[j];

    extent=0;
    giveUp=FALSE;
    distance=0;
    if(d == TOPEDGE || d == BOTTOMEDGE){
        while(extent < space[j].width && !giveUp){
            giveUp=TRUE;
            for(k=0;space[k].width;k++){
                if(k != j && isCurrent(space[k]) && isAdjacent(space[j],space[k]) == d){
                    if(space[j].x+extent == space[k].x){
                        extent+=space[k].width;
                        if(distance == 0){
                            distance=expanse(space,k,d);
                        }
                        else {
                            distance=min(distance,expanse(space,k,d));
                        }
                        giveUp=FALSE;
                    }
                    else if(space[j].x+extent > space[k].x && space[j].x+extent < space[k].x+space[k].width){
                        extent=space[k].x+space[k].width-space[j].x;
                        if(distance == 0){
                            distance=expanse(space,k,d);
                        }
                        else {
                            distance=min(distance,expanse(space,k,d));
                        }
                        giveUp=FALSE;
                    }
                }
            }
        }
        if(extent < space[j].width){
            return 0;
        }
        return space[j].height+distance;
    }
    else if(d == LEFTEDGE || d == RIGHTEDGE){
        while(extent < space[j].height && !giveUp){
            giveUp=TRUE;
            for(k=0;space[k].width;k++){
                if(k != j && isCurrent(space[k]) && isAdjacent(space[j],space[k]) == d){
                    if(space[j].y+extent == space[k].y){
                        extent+=space[k].height;
                        if(distance == 0){
                            distance=expanse(space,k,d);
                        }
                        else {
                            distance=min(distance,expanse(space,k,d));
                        }
                        giveUp=FALSE;
                    }
                    else if(space[j].y+extent > space[k].y && space[j].y+extent < space[k].y+space[k].height){
                        extent=space[k].y+space[k].height-space[j].y;
                        if(distance == 0){
                            distance=expanse(space,k,d);
                        }
                        else {
                            distance=min(distance,expanse(space,k,d));
                        }
                        giveUp=FALSE;
                    }
                }
            }
        }
        if(extent < space[j].height){
            return 0;
        }
        return space[j].width+distance;
    }
    return 0;
}
int match(rectangle *stack, rectangle *callstack, rectangle *space){
    int i,j,k,d,goal,mn;
    int height;
    int spacetxn, stacktxn, calltxn;
    int map;
    rectangle r;

    for(i=0,goal=0;space[i].width;i++){
        if(isCurrent(space[i])){
            goal+=space[i].width*space[i].height;
        }
    }
    if(goal == 0){
        return TRUE;
    }
    mn=minstack(stack);
    if(goal < mn){
        /* The goal (space available) is smaller than any rectangle left in the stack */
        return FALSE;
    }
    spacetxn=nexttransaction(space);
    stacktxn=nexttransaction(stack);
    calltxn=nexttransaction(callstack);
    for(j=0;space[j].width;j++){
        for(i=0;stack[i].width;i++){
            if(isCurrent(stack[i]) && isCurrent(space[j])){
                if(congruent(space[j], stack[i]) && adjacentrectangle(space,j,NOTYET) == NOTYET){
                    r=space[j];
                    r.created=calltxn;
                    rpush(callstack, r);
                    deleteTxn(stack[i],stacktxn);
                    deleteTxn(space[j],spacetxn);
                }
            }
        }
    }
    if(!notEmpty(space)){
        return TRUE;
    }
    rectangle e;
    for(j=0;space[j].width;j++){
        if(isCurrent(space[j])){
            e=space[j];
            for(k=0,map=0;space[k].width;k++){
                if(k != j && isCurrent(space[k])){
                    d=isAdjacent(space[j], space[k]);
                    if(d != NOTYET){
                        map|=d;
                    }
                }
            }
            if(bitcount(map) == 1){ /* space[j] has adjacent space on only one side */
                if(map == TOPEDGE || map == BOTTOMEDGE){
                    e.height=expanse(space,j,map);
                }
                else if(map == LEFTEDGE || map == RIGHTEDGE){
                    e.width=expanse(space,j,map);
                }
                for(i=0;stack[i].width;i++){
                    if(isCurrent(stack[i])){
                        if(congruent(e, stack[i])){
                            e.created=calltxn;
                            rpush(callstack, e);
                            deleteTxn(stack[i],stacktxn);
                            if(!removerectangle(space, e, spacetxn)){
                                printf("Logic error in match/expanse.  Terminating\n");
                                exit(0);
                            }
                            if(match(stack,callstack,space)){
                                return TRUE;
                            }
                            else {
                                rollback(stack,stacktxn);
                                rollback(callstack,calltxn);
                                rollback(space,spacetxn);
                                return FALSE;
                            }
                        }
                        else if(congruent(space[j], stack[i])){
                            r=space[j];
                            r.created=calltxn;
                            rpush(callstack, r);
                            deleteTxn(stack[i],stacktxn);
                            if(!removerectangle(space, r, spacetxn)){
                                printf("Logic error in match/expanse.  Terminating\n");
                                exit(0);
                            }
                            if(match(stack,callstack,space)){
                                return TRUE;
                            }
                            else {
                                rollback(stack,stacktxn);
                                rollback(callstack,calltxn);
                                rollback(space,spacetxn);
                                return FALSE;
                            }
                        }
                    }
                }
            }
        }
    }
    if(notEmpty(space)){
        rollback(stack,stacktxn);
        rollback(callstack,calltxn);
        rollback(space,spacetxn);
        return FALSE;
    }
    return TRUE;
}
int removerectangle(rectangle *space, rectangle r, int ntxn){
    int i,status=TRUE;
    for(i=0;space[i].width;i++){
        if(space[i].deleted == NOTYET){
            if(areEqual(space[i], r)){
                space[i].deleted=ntxn;
                return TRUE;
            }
            else if(containsRectangle(space[i], r)){
                if(r.x > space[i].x){
                    splitrectanglevertically(space, i, r.x, ntxn);
                }
                else if(r.y > space[i].y){
                    splitrectanglehorizontally(space, i, r.y, ntxn);
                }
                else if(r.x+r.width < space[i].x+space[i].width){
                    splitrectanglevertically(space, i, r.x+r.width, ntxn);
                }
                else if(r.y+r.height < space[i].y+space[i].height){
                    splitrectanglehorizontally(space, i, r.y+r.height, ntxn);
                }
            }
            else if(overlap(space[i], r)){  /* we have to split both */
                rectangle aux;
                if(r.x < space[i].x){
                    aux=r;
                    aux.width=space[i].x-r.x;
                    r.x+=aux.width;
                    r.width-=aux.width;
                    if(!removerectangle(space,aux,ntxn)){
                        return FALSE;
                    }
                }
                if(r.x+r.width > space[i].x+space[i].width){
                    aux=r;
                    aux.x=space[i].x+space[i].width;
                    aux.width=r.x+r.width-aux.x;
                    r.width-=aux.width;
                    if(!removerectangle(space,aux,ntxn)){
                        return FALSE;
                    }
                }
                if(r.y < space[i].y){
                    aux=r;
                    aux.height=space[i].y-aux.y;
                    r.y+=aux.height;
                    r.height-=aux.height;
                    if(!removerectangle(space,aux,ntxn)){
                        return FALSE;
                    }
                }
                if(r.y+r.height > space[i].y+space[i].height){
                    aux=r;
                    aux.y=space[i].y+space[i].height;
                    aux.height=r.y+r.height-aux.y;
                    r.height-=aux.height;
                    if(!removerectangle(space,aux,ntxn)){
                        return FALSE;
                    }
                }
                if(areEqual(space[i], r)){
                    space[i].deleted=ntxn;
                    return TRUE;
                }
                else {
                    if(!removerectangle(space,r,ntxn)){
                        return FALSE;
                    }
                    return TRUE;
                }
            }
        }
    }
    return TRUE;
}
int main(int argc, char *argv[]){
    int side=15;
    int n=5;
    int budget=0;
    int status;
    while((status=getopt(argc,argv,"l:n:")) >= 0){
        switch(status){
        case 'l':
            sscanf(optarg,"%d",&side);
            break;
        case 'n':
            sscanf(optarg,"%d",&n);
            break;
        }
    }
    budget=64;
    while(solve(side,n,budget) == FALSE){
        budget+=16;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.