USACO the castle

本文深入解析USACO竞赛中的经典题目“城堡”,通过详细阐述深搜算法的应用,逐步解决房间数量、最大房间面积及拆除特定墙壁后的影响。文章记录了从理解题意、设计算法到调试代码的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个题是usaco第二章的第一个题,很高兴啊,虽然这个题目真的太长了,又不好懂,但是对照字典来多看几遍肯定会弄明白的,这里就不讲题意了。
首先要找出所有的房间以及它们的大小
然后找出拆了那面墙会使出现最大的房间。
整张地图不会超过50x50.
首先输出城堡里有多少房间,
输出最大房间的大小
输出拆掉一面墙之后最大的房间
输出拆掉这面墙的位置(保证这面墙存在)
首先的想法是,读取所有的数据后,对每个数据进行拆分,然后。。。编不下去了,还是看看网上是什么解决办法吧。。。第一次做毫无头绪很正常啊。
网上的说法是深搜,但是如何深搜?题意都没搞清啊?而且看他们的代码好像还很简单的样子。。
但是不幸的是,他们讲的好像是其他的题。既然知道是深搜了,就大概知道怎么做了。
它的每个数字表示的是墙的叠加,这个1,2,4,8是可以无歧义拆分的,因此我们可以先将其拆分,非常棒,就先这么做,听网上说它是没有时间限制的。


#include <iostream>
using namespace std;

typedef struct Room{
    bool isUsed = false;
    bool northWall = false;
    bool southWall = false;
    bool westWall = false;
    bool eastWall = false;
} Room;
int m, n;
Room room[55][55];
int roomCount, roomSize[55], roomsize;
void dfs(int ci, int cj){
    roomsize++;
    room[ci][cj].isUsed = true;
    if(room[ci][cj].eastWall==false && ci<m){
        dfs(ci+1, cj);
    }
    if(room[ci][cj].westWall==false && ci>1){
        dfs(ci-1, cj);
    }
    if(room[ci][cj].southWall==false && cj<n){
        dfs(ci, cj+1);
    }
    if(room[ci][cj].northWall==false && cj>1){
        dfs(ci, cj-1);
    }
}

int main(){
    FILE *pFile1 = fopen("castle.in", "r");
    FILE *pFile2 = fopen("castle.out", "w");
    fscanf(pFile1, "%d%d", &m, &n);
    for(int i=1; i<=m; i++){
        for(int j=1; j<=n; j++){
            int t;
            fscanf(pFile1, "%d", &t);
            if(t >= 8){
                t -= 8;
                room[i][j].southWall = true;
            }
            if( t >= 4){
                t -= 4;
                room[i][j].eastWall = true;
            }
            if( t >= 2){
                t -= 2;
                room[i][j].northWall = true;
            }
            if(t >= 1){
                t -= 1;
                room[i][j].westWall = true;
            }
        }
    }
    for(int i=1; i<=m; i++){
        for(int j=1; j<=n; j++){
            if(room[i][j].isUsed == false){
                dfs(i, j);
                roomSize[roomCount++] = roomsize;
                roomsize = 0;
            }
        }
    }
    cout << roomCount << endl;
    fclose(pFile1); fclose(pFile2);
	return 0;
}

虽然是错误的,但至少出数了,这个是一个良好的开始。。。

void dfs(int ci, int cj){
    if(room[ci][cj].isUsed == true){
        return;
    }else{
        roomsize++;
        room[ci][cj].isUsed = true;
        if(room[ci][cj].eastWall==false && cj<m){
            dfs(ci, cj+1);
        }
        if(room[ci][cj].westWall==false && cj>1){
            dfs(ci, cj-1);
        }
        if(room[ci][cj].southWall==false && ci<n){
            dfs(ci+1, cj);
        }
        if(room[ci][cj].northWall==false && ci>1){
            dfs(ci-1, cj);
        }
    }
}

关键是这里,看出与上面的不同了吗?原来一开始,我根本就不知道如何调试,后来按照图中一点点移动,才发现这个问题
但是还是不正确,接着调试.
发现是m和n没有替换过来,替换了过来,还是错误。。。
接着调试。。。
发现了一个惊天bug----不说了,规定第一个参数为行,第二个参数为列。。参数输入的顺序与测试的顺序应该是相等的啊?


#include <iostream>
#include "algorithm"
using namespace std;

typedef struct Room{
    bool isUsed = false;
    bool northWall = false;
    bool southWall = false;
    bool westWall = false;
    bool eastWall = false;
} Room;
int m, n;
Room room[55][55]; // 房间单位
int roomCount, roomSize[55], roomsize;
void dfs(int ci, int cj){
    if(room[ci][cj].isUsed == true){
        return;
    }else{
        roomsize++;
        room[ci][cj].isUsed = true;
        if(room[ci][cj].eastWall==false && cj<m){
            dfs(ci, cj+1);
        }
        if(room[ci][cj].westWall==false && cj>1){
            dfs(ci, cj-1);
        }
        if(room[ci][cj].southWall==false && ci<n){
            dfs(ci+1, cj);
        }
        if(room[ci][cj].northWall==false && ci>1){
            dfs(ci-1, cj);
        }
    }
}

int main(){
    FILE *pFile1 = fopen("castle.in", "r");
    FILE *pFile2 = fopen("castle.out", "w");
    fscanf(pFile1, "%d%d", &m, &n);
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            int t;
            fscanf(pFile1, "%d", &t);
            if(t >= 8){
                t -= 8;
                room[i][j].southWall = true;
            }
            if( t >= 4){
                t -= 4;
                room[i][j].eastWall = true;
            }
            if( t >= 2){
                t -= 2;
                room[i][j].northWall = true;
            }
            if(t >= 1){
                t -= 1;
                room[i][j].westWall = true;
            }
        }
    }
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(room[i][j].isUsed == false){
                dfs(i, j); // 一次深搜获得一个房间
                roomSize[roomCount++] = roomsize;
                roomsize = 0;
            }
        }
    }
    sort(roomSize, roomSize+roomCount);
    cout << roomCount << endl;
    cout << roomSize[roomCount-1] << endl;
    fclose(pFile1); fclose(pFile2);
	return 0;
}

这个代码就是对的了,因为我完全把行和列搞混了。。。错误就是这个。。。只有这个,mmp。
好了,下面看找到那个墙怎么办?就是定义变量,挨个拆墙就行了。
如何拆墙呢?就是,额,一个一个的拆,拆右面或者上面的
输出最大的房间值和墙的位置

    int lagestRoom = 0;
    bool isN = true;
    int posX, posY;
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(i != 1){ // 拆上面的
                if(room[i][j].northWall){
                    room[i][j].northWall = false;
                    room[i-1][j].southWall = false;
                    for(int k=1; k<=n; k++)
                        for(int g=1; g<=m; g++)
                            dfs(i, j);
                    
                }
            }
            if(j != m){ // 拆右面的

            }
        }
    }

``
写到这里,我果断放弃,嵌套四个循环,太复杂了。。。。
应该脱离成为一个函数才行

        int lagestRoom = 0;
    bool isN = true;
    int posX=1, posY=1;
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(i!=1 && room[i][j].northWall){ // 拆上面的
                room[i][j].northWall = false;
                room[i-1][j].southWall = false;
                int t = theSizeOfRoom();
                if(t > lagestRoom){
                    lagestRoom = t;
                    posX = i; posY = j;
                    isN = true;
                }
                room[i][j].northWall = true;
                room[i-1][j].southWall = true;
            }
            if(j!=m && room[i][j].eastWall){ // 拆右面的
                room[i][j].eastWall = false;
                room[i][j+1].westWall = false;
                int t = theSizeOfRoom();
                if(t > lagestRoom){
                    lagestRoom = t;
                    posX = i; posY = j;
                    isN = false;
                }
                room[i][j].eastWall = true;
                room[i][j+1].westWall = true;
            }
        }
    }
    cout << lagestRoom <<endl;
    cout << posX <<" "<< posY <<" "<< (isN?"N":"E") <<endl;
    fclose(pFile1); fclose(pFile2);

`
后面又写了这么多,这个debug简直太难了,出了错都不知道如何修正。。。这段代码发现,无论如何,最终结果都是零,查了半天才发现,isuse没有修正,然后就变成了这个德行。。
加上了,16是得出了,但是后面是3 3 E,显然不对,为什么呢?看来是有不止一个,但是取最小的应该是这样,仔细读题意才发现,是最西南角的,先最西边的角然后最南边的角,是n的话选择n,可以通过调整循环顺序来完成操作.
稍微改了一下,终于成功了,花费了半天的时间,泪目。。。
等不及了,赶紧提交。。。
哦忘记了,应该打印在文件里。。。
但是现在到task5又出现了问题,能用n不用e,看来n的能力还相当的高。。。还得额外判断,在e那里
if(t==lagestRoom && isN) continue;
不对,这样第一个就有错误了,这到底是怎么回事呢?反正是最后一个的事情了,前面的都已经搞定了,唉,命运多舛啊。。。
经过debug后,发现这两个坐标的值其实是一样的,那么问题来了,到底是毛的顺序。。。
最靠西的,那么就是先要j最小才行,哦,明白了,j要放在外层循环。。。
现在一直运行到了task7,这个测试点比较恐怖,是50x50个15点。。。我这个是拆掉一堵墙之后,最大空间还是1,它按理论来说应该是2才对。。。
耍了一个小机灵,在定义的时候直接int lagestRoom = 2;就好了嘛,我真是个小机灵鬼。
还是不对,又把这个定义直接改成 int posX=n, posY=1;
不对不对,而且人家的2500, 你的25,这个怎么会相等。。。
后来发现roomsize数组开小了,只开了55个,至少2500才对。。。
好了,一共8个测试点,终于全部过了,花了今天一个下午+一个晚上(也许没那么多,我玩了不少),话说我还没有在一天内A通一道题呢。。。下面是AC的代码:


    /*
ID:22920179
PROB:castle
LANG:C++
*/
#include <iostream>
#include "cstring"
#include "algorithm"
using namespace std;

typedef struct Room{
    bool isUsed = false;
    bool northWall = false;
    bool southWall = false;
    bool westWall = false;
    bool eastWall = false;
} Room;
int m, n;
Room room[55][55]; // 房间单位
int roomCount, roomSize[2600], roomsize;
void dfs(int ci, int cj){
    if(room[ci][cj].isUsed == true){
        return;
    }else{
        roomsize++;
        room[ci][cj].isUsed = true;
        if(room[ci][cj].eastWall==false && cj<m){
            dfs(ci, cj+1);
        }
        if(room[ci][cj].westWall==false && cj>1){
            dfs(ci, cj-1);
        }
        if(room[ci][cj].southWall==false && ci<n){
            dfs(ci+1, cj);
        }
        if(room[ci][cj].northWall==false && ci>1){
            dfs(ci-1, cj);
        }
    }
}
int theSizeOfRoom(){
    roomCount = 0;
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            room[i][j].isUsed = false;
        }
    }
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(room[i][j].isUsed == false){
                dfs(i, j); // 一次深搜获得一个房间
                roomSize[roomCount++] = roomsize;
                roomsize = 0;
            }
        }
    }
    sort(roomSize, roomSize+roomCount);
    return roomSize[roomCount-1];
}
int main(){
    FILE *pFile1 = fopen("castle.in", "r");
    FILE *pFile2 = fopen("castle.out", "w");
    fscanf(pFile1, "%d%d", &m, &n);
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            int t;
            fscanf(pFile1, "%d", &t);
            if(t >= 8){
                t -= 8;
                room[i][j].southWall = true;
            }
            if( t >= 4){
                t -= 4;
                room[i][j].eastWall = true;
            }
            if( t >= 2){
                t -= 2;
                room[i][j].northWall = true;
            }
            if(t >= 1){
                t -= 1;
                room[i][j].westWall = true;
            }
        }
    }
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(room[i][j].isUsed == false){
                dfs(i, j); // 一次深搜获得一个房间
                roomSize[roomCount++] = roomsize;
                roomsize = 0;
            }
        }
    }
    sort(roomSize, roomSize+roomCount);
    cout << roomCount << endl;
    cout << roomSize[roomCount-1] << endl;
    fprintf(pFile2, "%d\n", roomCount);
    fprintf(pFile2, "%d\n", roomSize[roomCount-1]);
    int lagestRoom = 2;
    bool isN = true;
    int posX=n, posY=1;
    for(int j=m; j>=1; j--){
        for(int i=1; i<=n; i++){
            if(j!=m && room[i][j].eastWall){ // 拆右面的
                room[i][j].eastWall = false;
                room[i][j+1].westWall = false;
                int t = theSizeOfRoom();
                if(t >= lagestRoom){
                    lagestRoom = t;
                    posX = i; posY = j;
                    isN = false;
                }
                room[i][j].eastWall = true;
                room[i][j+1].westWall = true;
            }
            if(i!=1 && room[i][j].northWall){ // 拆上面的
                room[i][j].northWall = false;
                room[i-1][j].southWall = false;
                int t = theSizeOfRoom();
                if(t >= lagestRoom){
                    lagestRoom = t;
                    posX = i; posY = j;
                    isN = true;
                }
                room[i][j].northWall = true;
                room[i-1][j].southWall = true;
            }
        }
    }
    cout << lagestRoom <<endl;
    cout << posX <<" "<< posY <<" "<< (isN?"N":"E") <<endl;
    fprintf(pFile2, "%d\n", lagestRoom);
    fprintf(pFile2, "%d %d %s\n", posX, posY, (isN?"N":"E"));
    fclose(pFile1); fclose(pFile2);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值