这道题的解题思路?

littlemxz 2009-08-29 11:06:02
题目是acm的题目,pku1084-square destroyer
大概题意:给你N*N个1*1的小正方形组成的大正方形,问你破坏哪些边可以使得这个大正方形不再拥有任何子正方形。初始的时候,它可能会自动缺少某些边。
#include<iostream>
#include<bitset>
using namespace std;
__int64 rest, cover[60], remain[60];
int size, cnt, ans, up[6][6];
bool stick[60]; //记录每根火柴是否被取走
//记录每一行每一根火柴的序号
//例如size = 3时
//up[0][i] = 1, 2, 3(1 <= i <= 3)
//up[1][i] = 8, 9, 10(1 <= i <= 3)
//up[2][i] = 15, 16, 17(1 <= i <= 3)
void initUp()
{
for(int i = 0; i < size; i ++) {
up[i][0] = i * (2 * size + 1);
for(int j = 1; j < size; j ++)
up[i][j] = up[i][j - 1] + 1;
}
}
//记录以序号firststic为正方形左上方的火柴作为第一根火柴,边长为sqsize的正方形的状态
void initCover(int firststick,int sqsize,__int64 mask)
{
int gap = 2 * size + 1;
int num = firststick;
for(int i = 0; i < sqsize; i ++) { //记录水平边的的火柴的状态
//cover[i]记录能由第i根火柴组成的正方形的状态
cover[num] |= mask; //第num根火柴是这个正方形的组成部分
cover[num + sqsize * gap] |= mask;
num ++;
}
num = firststick+size;
for(int i = 0; i < sqsize; i ++) { //记录竖边的火柴的状态
cover[num] |= mask;
cover[num + sqsize] |= mask;
num += gap;
}
}
void input()
{
int i, j, k, n;
cout<<"请输入正方形的边长:";
cin >> size; //整个正方形的边长
initUp();//记录每一行每一根火柴的序号
cout<<"请输入缺失火柴的数目:";
cin >> cnt; //缺失的火柴的根数
memset(stick, true, sizeof(stick));
if(cnt!=0) cout<<"请输入缺失火柴的编号:";
for(i = 0; i < cnt; i ++) {
cin >> n; //缺失的火柴的编号
stick[n - 1] = false; //被拿掉的火柴
}
__int64 mask = 1;
memset(cover, 0, sizeof(cover));
for(i = 1; i <= size; i ++) { //边长为i的正方形的状态
for(j = 0; j <= size - i; j ++) {
for(k = 0; k <= size - i; k ++) {
initCover(up[j][k], i, mask);
mask <<= 1; //mask = 1 << k表示第k个正方形
}
}
}
}
void modify()
{
int i, j, rec = 0;
for(i = 1; i <= size; i ++) rec += i * i;//正方形个数
rest = 1;
for(i = 1; i < rec; i ++) {//将rest的二进制每一位都设置为1
//由于size最多只到5,则rec 最多为63,故64位数足够表示
rest <<= 1;//左移1
rest += 1;//再加1
}
cnt = 2 * size * (size + 1); //记录火柴的根数
for(i = 0; i < cnt; i ++) {
if(! stick[i]) rest &= ~ cover[i]; //记录剩下的火柴组成的状态
}
for(i = 0; i < cnt; i ++) {
cover[i] &= rest; //由于有些火柴已被取掉,故必须更新cover[i]
}

for(i = 0; i < cnt; i ++) {
if(! stick[i]) continue;
for(j = 0; j < cnt; j ++) {
if(j != i && (cover[i] & cover[j]) == cover[j])//去掉重复集合
stick[j] = false;
}
}

int c = 0;
for(i = 0; i < cnt; i ++) {
//只记录有效的stick[i]的cover[i]状态
if(stick[i]) cover[c ++] = cover[i];
}
cnt = c;//重置火柴总数
remain[cnt - 1] = cover[cnt - 1];
for(i = cnt - 2; i >= 0; i --) {
//remain[i]表示第i根火柴和尚未被取走的火柴所能组成的正方形
remain[i] = remain[i + 1] | cover[i];
}
}
void dfs(int c,int last,__int64 state)
{
//假设当前取走火柴的方案已经能破坏掉剩下的所有正方形,则进行更新当前最优解
if(state == rest) ans = c;
else {
if(c < ans && (remain[last + 1] | state) == rest) {
for(int i = last + 1; i < cnt; i ++) {
//试着拿掉cover[i]所表示的火柴,这样所破坏掉的正方形就为state | cover[i]
if((cover[i] | state) > state)
dfs(c + 1, i, state | cover[i]);
//bitset<64> b(cover[i]);
//cout<<b<<endl;


}
}
}

}
int main()
{
input();
modify();
ans = 25;
dfs(0, -1, 0);
cout<<"至少拿走的火柴数目:";
cout << ans << endl;

system("pause");
return(0);
}

谁具体说一下解题思路么?
谢谢啦 。
...全文
128 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
littlemxz 2009-08-29
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 liao05050075 的回复:]
看一看别人的解题报告吧
http://www.baidu.com/s?ie=gb2312&bs=pku1084-square+destroyer&sr=&z=&cl=3&f=8&tn=baidu&wd=square+destroyer&ct=0
[/Quote]
看过了,不够具体。。。
liao05050075 2009-08-29
  • 打赏
  • 举报
回复
看一看别人的解题报告吧
http://www.baidu.com/s?ie=gb2312&bs=pku1084-square+destroyer&sr=&z=&cl=3&f=8&tn=baidu&wd=square+destroyer&ct=0
wanjingwei 2009-08-29
  • 打赏
  • 举报
回复
帮顶
littlemxz 2009-08-29
  • 打赏
  • 举报
回复
没人有空么?

64,649

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧