题意

一个\(N*M\)的矩阵,其中“.”代表空地,“0-9”代表古代建筑,我们如果选择了一个编号的古代建筑想要建立,那么对应就要将全部该编号的建筑建立起来,如果在空地上建筑,只建立当前点。问最多能够建立多少种建筑,并且每两种建筑之间没有公共边

分析

注意了,古代建筑的公共边是他的事,你不能选两个相邻的古代建筑。那么,我们把古代建筑抽象成一个点, 把剩下的各个现代的农场块抽象成各个点,给曼哈顿距离为1的块连一条边,那么这条题目就成了在这个新图上找图的最大独立集(注意了,这里和其他的题解不太一样,这里没有保证建的模型是二分图,所以仅仅是求图的最大独立集)。

那么如何求图的最大独立集?在这里总结一下三种最大独立集的写法。

  1. 树的最大独立集。这是树形dp的经典问题,定义\(d[i]\)为以\(i\)为节点的子树的最大独立集大小,那么对于i节点,我们可以去考虑两种决策选或不选。如果选,它的儿子节点没法选了(也就是考虑它的孙子节点),但是答案+1;否则就可以考虑它的儿子节点,但是这个节点对答案不产生贡献。这样一来,就有了dp公式:$$d[i]=max(C_{max}[i],G_{max}[i]+1)$$,其中\(C_{max}[i]\)为i的孩子的dp和,\(G_{max}[i]\)为i的孙子的dp和。如果无根树先转化成有根树。典型问题见我POJ-2342的题解。
  2. 二分图的最大独立集。这同样是二分图的经典问题,\(n\)减去最大匹配数就可以了,简单说下原因(具体的见我HDU-1068的题解):对于非最大匹配上的点他们一定是彼此独立的(如果有两个空点相连就存在增广路了),那么再加上最大匹配的彼此一个点,就是它的最大独立集。
  3. 图的最大独立集。这是这题的写法。图的最大独立集转化为补图的最大团来解决。最大团的定义见我POJ-2989的题解。典型问题见我POJ-1419的题解。

然后简单的说下我这里最大团的求法。这位dalao的博客给我很多启发。下标从0开始:

定义\(dp[i]\)是\(i\)点从自己到\(n-1\)这些点的最大团的节点个数(用mx更新)。然后在dfs中枚举每一个节点,看这些节点与哪些编号比它大的点相连,然后记录他们(按照递归深度在stk数组)并递归处理之。

接下来考虑剪枝(不然复杂度是\(O(n2^n)\),最大团是个NP完全问题),简单地,如果目前的最大团个数加上自己要去的节点之后的哪怕所有点都无法比当前最优解更好,剪枝;类似地,如果当前个数加上之后要去的节点到最后的最大团个数还是不如最优解,剪枝。

然后这题就写出来了。

另一种写法

对于这样的一个矩阵,如果没有古代建筑,二分图是有经典建模的:根据\((i+j)\%2\)来把矩阵的点分割成两个点集,边仍然是按照上面那个曼哈顿距离的思路做建模。然后很好做了,跑一遍最大匹配,然后n*m-最大匹配/2(记得除以2,无向图)就可以了。

那么这里既然有不超过10个古代建筑,暴力枚举之,然后把所有古代建筑的点与选中古代建筑的曼哈顿距离为1的格子去掉然后做最大匹配,按照没有古代建筑的思路做就可以了。网上有代码。

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <map>
#define MP make_pair
#define PB emplace_back
#define fi first
#define se second
#define ZERO(x) memset((x), 0, sizeof(x))
#define ALL(x) (x).begin(),(x).end()
#define rep(i, a, b) for (repType i = (a); i <= (b); ++i)
#define per(i, a, b) for (repType i = (a); i >= (b); --i)
#define QUICKIO \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
using namespace std;
using ll=long long;
using repType=int; const int MAXN=105;
int mat[12][12];
vector<int> vec;
map<int, int> idx;
int n,m;
bool graph[MAXN][MAXN];
int dp[MAXN];
int mx;
int stk[MAXN][MAXN]; void
init()
{
ZERO(mat);
memset(graph,true,sizeof(graph));
vec.clear();
idx.clear();
} const int dx[]={1,0,-1,0},
dy[]={0,1,0,-1}; int query(int x)
{
if(idx.find(x)==idx.end())
{
vec.PB(x);
return idx[x]=int(vec.size())-1;
}
else return idx[x];
} void dfs(int N,int num,int step)
{
if(num==0)
{
if(step>mx)
{
mx=step;
}
return;
}
for(int i=0;i<num;i++)
{
int k = stk[step][i];
if(step+N-k<=mx) return ;
if(step+dp[k]<=mx) return ;
int cnt = 0;
for(int j=i+1;j<num;j++)
if(graph[k][stk[step][j]])
{
stk[step+1][cnt++]=stk[step][j];
}
dfs(N,cnt,step+1);
}
} void run(int N)
{
mx=0;
for(int i=N-1;i>=0;i--)
{
int sz=0;
for(int j=i+1;j<N;j++)
if(graph[i][j]) stk[1][sz++]=j;
dfs(N,sz,1);
dp[i]=mx;
}
} int
main()
{
QUICKIO
int T; cin>>T;
rep(kase,1,T)
{
init();
cin>>n>>m;
int blank_area=0;
rep(i,1,n) rep(j,1,m)
{
char chr;
cin>>chr;
if(chr=='.') mat[i][j]=10+(blank_area++);
else
{
mat[i][j]=chr-'0';
/*
if(ma.find(mat[i][j])==ma.end())
{
vec.PB(mat[i][j]);
ma[mat[i][j]]=int(vec.size())-1;
}
*/
}
}
rep(i,1,n) rep(j,1,m)
{
int gx=query(mat[i][j]);
rep(k,0,3)
{
int ti=i+dx[k],
tj=j+dy[k];
if(ti>=1 && ti<=n && tj>=1 && tj<=m)
{
int gy=query(mat[ti][tj]);
if(gx!=gy)
graph[gx][gy]=graph[gy][gx]=false;
}
}
}
int gsz=vec.size();
run(gsz);
cout<<"Case #"<<kase<<": "<<dp[0]<<endl;
}
return 0;
}

「国庆训练&知识学习」图的最大独立集与拓展(Land of Farms,HDU-5556)的更多相关文章

  1. 「日常训练&知识学习」单调栈

    这几天的知识学习比较多,因为时间不够了.加油吧,为了梦想. 这里写几条简单的单调栈作为题解记录,因为单调栈的用法很简单,可是想到并转化成用这个需要一些题目的积淀. 相关博客参见:https://blo ...

  2. 「日常训练&知识学习」树的分块(王室联邦,HYSBZ-1086)

    题意与分析 这题的题意就是树分块,更具体的看题目(中文题). 学习这一题是为了树的分块,为树上莫队做铺垫. 参考1:https://blog.csdn.net/LJH_KOQI/article/det ...

  3. 「日常训练&知识学习」树的直径(POJ-1849,Two)

    题意 一个城市由节点和连接节点的街道组成,街道是双向的. 此刻大雪覆盖了这个城市,市长确定了一些街道要将它们清扫干净,这些街道保证所有的节点可以通过它们连通而且街道数目尽可能小. 现有两台相同的扫雪机 ...

  4. 「日常训练&知识学习」莫队算法(二):树上莫队(Count on a tree II,SPOJ COT2)

    题意与分析 题意是这样的,给定一颗节点有权值的树,然后给若干个询问,每次询问让你找出一条链上有多少个不同权值. 写这题之前要参看我的三个blog:Codeforces Round #326 Div. ...

  5. 「国庆训练」Bomb(HDU-5934)

    题意 给定\(n\)个炸弹,每个炸弹的坐标与代价与影响范围给定,炸弹会引爆影响范围内其他所有炸弹.求引爆所有炸弹的最小代价. 分析 先做\(n^2\)的循环,然后建图,对\(i\)能引爆\(j\)建边 ...

  6. 「国庆训练」ArcSoft's Office Rearrangement(HDU-5933)

    题目与分析 题解见https://blog.csdn.net/cmershen/article/details/53200922. 训练赛场上我们写出来了--在4小时50分钟的时候...激情补题啊.. ...

  7. 「国庆训练」Kingdom of Obsession(HDU-5943)

    题意 给定\(s,n\),把\(s+1,s+2,...,s+n\)这\(n\)个数填到\(1,2,...,n\)里,要求\(x\)只能填到\(x\)的因子的位置(即题目中\(x\%y=0\)那么x才能 ...

  8. 「知识学习」二分图的最大匹配、完美匹配和匈牙利算法(HDU-2063)

    定义 如果一个图\((E,V)\)的顶点集\(E\)能够被能够被分成两个不相交的集合\(X,Y\),且每一条边都恰连接\(X,Y\)中的各一个顶点,那么这个图就是一个二分图. 容易得知,它就是不含有奇 ...

  9. 精心整理「服务器Linux C/C++」 成长路程(附思维导图)

    前言 我不是名校毕业,更没有大厂的背景,我只是一个毕业不到 2 年的普普通通的程序员,在摸爬滚打的工作这段时间里,深知了有一个「完整的知识体系」是非常重要的.当事人非常后悔没有在大学期间知道这个道理- ...

随机推荐

  1. protobuf编码

     proto2 Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化,适合做数据存储或 RPC 数据交换格式.可用于通讯协议.数据存储等领域的语言无关.平台无 ...

  2. 【转】Android开发:shape和selector和layer-list的(详细说明)

    <shape>和<selector>在Android UI设计中经常用到.比如我们要自定义一个圆角Button,点击Button有些效果的变化,就要用到<shape> ...

  3. tp3.2中前台模板中日期时间的转换

    {$vo.create_time|date='Y-m-d',###} 其中###是占位符

  4. Entity Framework——读写分离

    1 实现 CustomDbContext扩展了DbContext,其构造函数带有形式参nameOrConnectionString,可以在使用CustomDbContext时指定数据库连接字符串. D ...

  5. java eclipse 访问 Oracle数据库的代码

    package com.hanqi.test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.R ...

  6. Knowledge Point 20180309 字符串常量池与String,intern()

    引言 什么都先不说,先看下面这个引入的例子: public static void test4(){ String str1 = new String("SEU") + new S ...

  7. ORACLE_HOME_LISTNER is not SET, unable to auto-start Oracle Net Listener

    执行$ORACLE_HOME/bin/dbstart 启动数据库提示如下: [oracle@prim bin]$ ./dbstart ORACLE_HOME_LISTNER is not SET, u ...

  8. 产品 | What's产品经理

    如果想知道什么是产品,首先需要知道什么是缔造者.其名曰:"产品经理". PS:产品经理一词在国内大多时候泛指"互联网产品经理". 对于产品经理这一职位,说实在很 ...

  9. chromium之message_pump_win之一

    写了22篇博文,终于到这里了———— MessagePumpWin!!! MessagePumpWin这个类还是挺复杂的,可以分成好几部分.接下来分块分析 从介绍看,MessagePumpWin 是M ...

  10. Python 1.1数字与字符基础

    一. 基础数字操作 1.加减乘除以及内置函数: min(),  max(),  sum(),  abs(),  len()         math库: math.pi math.e, math.si ...