O興~
 
« 上一篇: mplayer播放mp3或某些视频文件全是杂音的解决方法 下一篇: 为什么可以while(cin >> a) »
我很懒的 @ 2006-12-20 14:42

    这又是一道我以前一直没过最近又做了一次就过掉的题目。地址在这里
    大致题意就是一些火柴棍拼成了许多正方形,问最少拿掉几根可以把所有正方形都破坏掉。其实背后的模型就是说给你若干个子集,要求取最少个数的子集,使它们能覆盖整个全集。同样模型的题目还有UVA 10160
    首先要初始化一下,把重叠的或者有包含关系的子集都去掉,比如A = B或者A包含于B,则A就可以去掉不要考虑了。然后然后用DFS加剪枝就可以过。基本的DFS我就不说了。当DFS到某一个节点时,如果发生以下情况之一,则该节点就不要再往下发展了,也就是所谓的剪枝:
    1. 当前已取的子集数量超过了先前已搜到的最优解,则再发展下去也不可能搜出更优的解了。这个最优解的初始值是所有子集的总数(所有子集都取了总归能覆盖全集了吧)。
    2. 在当前一步的基础上取了某个子集后,覆盖的集合没有发生变改,则新取的这个子集是没有义意的,所以就不要在它身上发展下一层节点了。
    3. 在当前已经覆盖的集合的基础上,即使把之后所有的子集都加上也无法覆盖整个全集的话,那也没有必要搜下去了。
   
    有了这几个剪枝,在时间数量级上就足够快了。但是要真正的速度够快的话还需要一些常数级的优化,比如:
    1. 用位操作表示集合的交、并、补。
    2. 上面说的第3个剪枝当中,"之后所有的子集所能覆盖的集合"可以在一开使就计算好,以避免重复计算。

    另外,还有一个优化,就是一开始把所有子集按基数(元素的个数)从大到小排序。搜的每一步总是先取基数最大的一个子集。这样比较有可能较早地找到最优解,当然只是可能而已。
   
    有了这些优化就差不多了。虽然极限数据过不了,但是这道题本身的测试数据很弱,我只用了0.031秒。

    PS. 2008-SEP-03. 应某网友要求,贴一下代码,如下:(现在看自己以前的代码,风格都有点不认识了。

#include <iostream>
#include <cstring>

using namespace std;

const int MAX_SIZE = 5;
const int MAX_STK = 60;

typedef unsigned long long ULL;

class CoverInit {
private:
static int m_size;
//Number of the stick on the top of the grid [row][col].
static int m_up[MAX_SIZE][MAX_SIZE];

static void initUp() {
for (int i = 0; i < m_size; i++) {
m_up[i][0] = i * (2 * m_size + 1);
for (int j = 1; j < m_size; j++) {
m_up[i][j] = m_up[i][j - 1] + 1;
}
}
}

static void initSqr(int firstStick, int size, ULL mask, ULL* cover) {
int gap = 2 * m_size + 1;
int num = firstStick;//The upper and the lower edges.
for (int i = 0; i < size; i++) {
cover[num] |= mask;
cover[num + size * gap] |= mask;
num++;
}
num = firstStick + m_size;//The left and the right edges.
for (int i = 0; i < size; i++) {
cover[num] |= mask;
cover[num + size] |= mask;
num += gap;
}
}

public:
static void initCover(int size, ULL* cover, int stkCnt) {
m_size = size;
initUp();
memset(cover, 0, stkCnt * sizeof(ULL));
ULL mask = 1ULL;
for (int sqrSize = 1; sqrSize <= m_size; sqrSize++) {
for (int i = 0; i <= m_size - sqrSize; i++) {
for (int j = 0; j <= m_size - sqrSize; j++) {
int up = m_up[i][j];
initSqr(up, sqrSize, mask, cover);
mask <<= 1;
}
}
}
}
};

int CoverInit::m_size;
int CoverInit::m_up[MAX_SIZE][MAX_SIZE];

class Solution {
private:
ULL m_cover[MAX_STK];
bool m_stick[MAX_STK];
ULL m_universal;
int m_sqrCnt;
int m_stkCnt;
int m_minStk;
ULL m_remainCov[MAX_STK];

void dfs(char stkCnt, char lastStk, ULL cover) {
if (cover == m_universal) {
m_minStk = stkCnt;
}
else {
if (stkCnt < m_minStk
&& (m_remainCov[lastStk + 1] | cover) == m_universal
) {
for (int i = lastStk + 1; i < m_stkCnt; i++) {
if ((m_cover[i] | cover) > cover) {
dfs(stkCnt + 1, i, cover | m_cover[i]);
}
}
}
}
}

void init(int size) {
m_stkCnt = 2 * size * (size + 1);

CoverInit::initCover(size, m_cover, m_stkCnt);

int sqrCnt = 0;
for (int i = 1; i <= size; i++) {
sqrCnt += i * i;
}
m_universal = 1ULL;
for (int i = 1; i < sqrCnt; i++) {
m_universal <<= 1;
m_universal += 1ULL;
}

for (int i = 0 ; i < m_stkCnt; i++) {
m_stick[i] = true;
}
}

void modifyData() {
for (int i = 0; i < m_stkCnt; i++) {
if (!m_stick[i]) {
m_universal &= ~m_cover[i];
}
}

for (int i = 0; i < m_stkCnt; i++) {
m_cover[i] &= m_universal;
}
for (int i = 0; i < m_stkCnt; i++) {
if (m_stick[i]) {
for (int j = 0; j < m_stkCnt; j++) {
if (j != i && m_stick[j]
&& m_cover[j] == (m_cover[j] & m_cover[i])
) {
m_stick[j] = false;
}
}
}
}
int cnt = 0;
for (int i = 0; i < m_stkCnt; i++) {
if (m_stick[i]) {
m_cover[cnt] = m_cover[i];
cnt++;
}
}
m_stkCnt = cnt;

m_remainCov[m_stkCnt - 1] = m_cover[m_stkCnt - 1];
for (int i = m_stkCnt - 2; i >= 0; i--) {
m_remainCov[i] = m_remainCov[i + 1] | m_cover[i];
}
}

public:
void input() {
int size;
cin >> size;
init(size);

int rmCnt;
cin >> rmCnt;
for (int i = 0; i < rmCnt; i++) {
int rm;
cin >> rm;
m_stick[rm - 1] = false;
}
}

void search() {
modifyData();
m_minStk = m_stkCnt < 9? m_stkCnt: 9;
dfs(0, -1, 0ULL);
cout << m_minStk << endl;
}
};

int main() {
Solution solution;
int caseCnt;
cin >> caseCnt;
for (int i = 0; i < caseCnt; i++) {
solution.input();
solution.search();
}
return 0;
}



最新评论


acmer

2007-03-28 13:28

Thank you for your advice

不用谢。祝你通过。


2008-09-03 18:56 匿名 125.217.*.*

能给出答案吗?

我已经把代码贴出来了。希望能有所帮助。


wolf

2008-10-07 16:27 匿名 58.62.*.*

thank you very much
我用你的第二个剪枝技巧,在ZOJ上勉强6秒过(ZOJ上time limit 是10秒)

但是你说的 背后的模型就是说给你若干个子集,要求取最少个数的子集,使它们能覆盖整个全集。
一点也不能理解,能教教我吗??
如果你有时间

可能我这句话讲得不清楚。比如说全集是{1,2,3,4,5};现在有若干个子集A{1,2,4}、B{1,3}、C{1,4,5}、D{2,5}、E{3,4,5}。要求在A、B、C、D、E中任取若干个,可以把{1,2,3,4,5}揍齐;但是要取得尽量少。所以就选A、E,两个就够了。


fangkyo

2009-02-05 01:43 匿名 123.148.*.*

我觉得这个题目是不是用贪心做好呢,总是去掉能够摧毁最多方块的火柴直到没有完整的方块为止

这个模型是一个经典的问题,有很多反例可以说明贪心不一定能得到正解的。

评论 / 个人网页 / 扔小纸条
* 昵称

已经注册过? 请登录

新用户请先注册 以便能显示头像及追踪评论回复

Email
网址
* 评论
表情
 


 

分类小组论坛
杂谈 , 娱乐、八卦 , 文学、艺术 , 体育 , 旅游、同城 , 象牙塔 , 情感 , 时尚、生活 , 星座 , 科技

请注意遵守中华人民共和国法律法规, 如威胁到本站生存, 将依法向有关部门报告, 同时本站的相关记录可能成为对您不利的证据.

相关法律法规
全国人大常委会关于维护互联网安全的决定
中华人民共和国计算机信息系统安全保护条例
中华人民共和国计算机信息网络国际联网管理暂行规定
计算机信息网络国际联网安全保护管理办法
计算机信息系统国际联网保密管理规定

日历
网志分类
· 所有网志
· program
· 未分类
站内搜索
友情链接
· 歪酷博客
· 管理我的Blog
· jamie
· feemi
· lyrist
· tata
· allen
· richard

订阅 RSS

0081321

歪酷博客