HNOI2006 潘多拉的盒子
题解:
题目的描述比较长,理解起来也有一定难度。仔细读题后我们发现整个任务可以分成两个部分:找出咒语机之间所有的升级关系、求最长升级序列。
1、 求升级关系:
容易看出,咒语机i可以抽象成一个图Gi,其顶点集Vi为ni个元件,每个顶点发出两条边——“0”边和“1”边,分别表示将信号加“0”和加“1”。
我们枚举两个咒语机A、B(A≠B),判断B是否是A的升级。最简单的想法是生成出A和B的所有咒语源,然后判断前者是否为后者的子集。但是,一个咒语机产生的咒语源可能有无限多个,无法逐一判断。
其实,只要存在一个咒语源,A能够产生而B不能产生,那么这一升级关系就不成立。为了找到(或者证明不存在)这样的一个咒语源,我们构造图H,其顶点是一个二元组(i,j),表示图GA的顶点i和图GB的顶点j。如果图GA的顶点i走“c”边到达顶点ic(c=0,1),图GB的顶点j走“c”边到达顶点jc,那么从图H的顶点(i,j)连有向边到(ic,jc)。我们将图H中的某些顶点(i,j)称为“关键顶点”,其特点是:图GA的顶点i是输出元而图GB的顶点j不是输出元。存在一个A能够产生而B不能的咒语源,等价于图H中存在从顶点(0,0)到关键顶点的路径。我们只需用广度优先搜索遍历图H即可。
2、 求最长升级序列:
假设第1部分求出的升级关系保存在图G中:如果咒语机B是A的升级,那么图G中从A向B连一条有向边。最长升级序列在图中对应最长路经。如果G是有向无环图,那么可以用拓扑排序加上动态规划的方法求最长路径。可惜的是,G有可能存在环,并且只有一种可能:存在若干个咒语机,它们产生的咒语源完全相同。显然,在这种情况下,只要选择其中一个,那么与之相同的所有咒语机都可以被选择。因此,我们把图中所有相同的咒语机合并成一个结点,并用num域记录该结点是由多少个结点合并而来(如图1)。
这样,问题转化为在一个有向无环图中求带权最长路经,这同样可以用拓扑排序加上动态规划的方法解决。具体的方法是:首先将图的顶点重新编号使得1,2,…,n是图的拓扑序,然后利用状态转移方程求解即可。
以上两个部分中,第一部分的时间复杂度为O(n2s2),第二部分的时间复杂度为O(s2),所以算法的总时间复杂度为O(n2s2)。
代码:
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = ;
int s;
struct node
{
int n,m;
bool ot[N];
int ch[N][];
void read()
{
scanf("%d%d",&n,&m);
for(int a,i=;i<=m;i++)
{
scanf("%d",&a);
ot[a]=;
}
for(int i=;i<n;i++)
{
scanf("%d%d",&ch[i][],&ch[i][]);
}
}
}p[N];
int hed[N],cnt;
struct EG
{
int fr,to,nxt;
}e[N*N],e0[N*N];
void ae(int f,int t)
{
e[++cnt].fr = f;
e[cnt].to = t;
e[cnt].nxt = hed[f];
hed[f] = cnt;
}
struct Pair
{
int x,y;
Pair(){}
Pair(int x,int y):x(x),y(y){}
};
bool vs[N][N];
bool check(node &a,node &b)
{
memset(vs,,sizeof(vs));
queue<Pair>q;
q.push(Pair(a.ch[][],b.ch[][]));
q.push(Pair(a.ch[][],b.ch[][]));
while(!q.empty())
{
Pair u = q.front();
q.pop();
if(!a.ot[u.x]&&b.ot[u.y])return ;
if(vs[u.x][u.y])continue;
vs[u.x][u.y]=;
q.push(Pair(a.ch[u.x][],b.ch[u.y][]));
q.push(Pair(a.ch[u.x][],b.ch[u.y][]));
}
return ;
}
int dep[N],low[N],tot;
bool vis[N];
int bel[N],bc,siz[N],sta[N],tl;
void tarjan(int u)
{
dep[u]=low[u]=++tot;
vis[u]=;
sta[++tl] = u;
for(int j=hed[u];j;j=e[j].nxt)
{
int to = e[j].to;
if(!dep[to])
{
tarjan(to);
low[u] = min(low[u],low[to]);
}else if(vis[to])
{
low[u] = min(low[u],dep[to]);
}
}
if(dep[u]==low[u])
{
bc++;
int c = -;
while(c!=u)
{
c = sta[tl--];
bel[c]=bc;
siz[bc]++;
vis[c] =;
}
}
}
bool eg[N][N];
int Hed[N],Cnt;
void AE(int f,int t)
{
e0[++Cnt].to = t;
e0[Cnt].nxt = Hed[f];
Hed[f] = Cnt;
}
int dp[N];
int dfs(int u)
{
if(dp[u])return dp[u];
if(!Hed[u])return dp[u]=siz[u];
for(int j=Hed[u];j;j=e0[j].nxt)
dp[u]=max(dp[u],dfs(e0[j].to)+siz[u]);
return dp[u];
}
int main()
{
// freopen("pandora.in","r",stdin);
// freopen("pandora.out","w",stdout);
scanf("%d",&s);
for(int i=;i<=s;i++)p[i].read();
for(int i=;i<=s;i++)
for(int j=;j<=s;j++)
if(i!=j&&check(p[i],p[j]))
ae(i,j);
for(int i=;i<=s;i++)
if(!dep[i])tarjan(i);
for(int j=;j<=cnt;j++)
{
int f = bel[e[j].fr],t = bel[e[j].to];
if(f!=t&&!eg[f][t])eg[f][t]=,AE(f,t);
}
int ans = ;
for(int i=;i<=bc;i++)ans=max(ans,dfs(i));
printf("%d\n",ans);
return ;
}
HNOI2006 潘多拉的盒子的更多相关文章
- 图论(Tarjan缩点):BZOJ 1194: [HNOI2006]潘多拉的盒子
1194: [HNOI2006]潘多拉的盒子 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 344 Solved: 181[Submit][Stat ...
- BZOJ 1194: [HNOI2006]潘多拉的盒子( BFS + tarjan + dp )
O(S²)枚举2个诅咒机, 然后O(n²)BFS去判断. 构成一个有向图, tarjan缩点, 然后就是求DAG的最长路.. ------------------------------------- ...
- 1194: [HNOI2006]潘多拉的盒子
1194: [HNOI2006]潘多拉的盒子 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 464 Solved: 221[Submit][Stat ...
- BZOJ1194: [HNOI2006]潘多拉的盒子(tarjan)
Description 传说中,有个神奇的潘多拉宝盒.如果谁能打开,便可以拥有幸福.财富.爱情.可是直到真的打开,才发现与之 相随的还有灾难.不幸.其实,在潘多拉制造这个宝盒的时候,设置了一些咒语来封 ...
- 1194: [HNOI2006]潘多拉的盒子 - BZOJ
Description Input 第一行是一个正整数S,表示宝盒上咒语机的个数,(1≤S≤50).文件以下分为S块,每一块描述一个咒语机,按照咒语机0,咒语机1„„咒语机S-1的顺序描述.每一块的 ...
- BZOJ 1194: [HNOI2006]潘多拉的盒子 [DP DFA]
传送门 题意: s个DFA,选出尽量多的自动机a0, a1, a2, . . . , at,使得a1包含a0.a2包 含a1,以此类推.s ≤ 50. DFA的字符集为{0,1},有的节点是输出源,节 ...
- 【强连通分量】Bzoj1194 HNOI2006 潘多拉的盒子
Description Sulotion 首先要对每对咒语机建图,判断机器a是否能生成所有机器b生成的 如果跑一个相同的串,最后结束的点b可输出a不可输出,判断就为否 大概就用这种思路,f[x][y] ...
- 【bzoj1194】 HNOI2006—潘多拉的盒子
http://www.lydsy.com/JudgeOnline/problem.php?id=1194 (题目链接) 题意 给出S个自动机,如果一个自动机u的所有状态是另一个自动机v的状态的子集,那 ...
- BZOJ 1194 [HNOI2006]潘多拉的盒子 (图论+拓扑排序+tarjan)
题面:洛谷传送门 BZOJ传送门 标签里三个算法全都是提高组的,然而..这是一道神题 我们把这道题分为两个部分解决 1.找出所有咒语机两两之间的包含关系 2.求出咒语机的最长上升序列 我们假设咒语机$ ...
随机推荐
- Android Studio提示忽略大小写
Android Studio的自动提示功能非常之强大,但是,如果你要输入“String”,你输入“string”,这个是不会提示的,也就是大小写敏感的,不爽是吗? 选择大小写不敏感就ok了!这样你想怎 ...
- hdoj5301
题意: 有一个n*m的大矩阵, 其中有一个1*1的不要的位置(x,y), 然后用若干个小矩阵去覆盖大矩阵, 不要的不能被覆盖. 问小矩阵中面积最大的面积最小是多少. 思路: 巨巨先画一个矩形,看看那个 ...
- noi.ac 邀请赛1 By cellur925
A. array 考场:上来就想暴力,首先第一个子任务肯定没问题,怎么搞都行.然后第二个子任务用个数组记下新修的值就行了.第三个子任务用一下等差数列求和公式帮助求解,每次都重新算(因为每次改变全部元素 ...
- hbase表结构 + hbase集群架构及表存储机制
本博文的主要内容有 .hbase读取数据过程 .HBase表结构 .附带PPT http://hbase.apache.org/ 读写的时候,就需要用hbase了,换句话说,就是读写的时候.需要 ...
- Qt样式表之三:实现按钮三态效果的三种方法
按钮的三态,指的是普通态.鼠标的悬停态.按下态.Qt中如果使用的是默认按钮,三态的效果是有的,鼠标放上去会变色,点击的时候有凹陷的效果. 但是如果自定义按钮实现三态效果有三种方法,一种是设置背景图,主 ...
- 大数模板 (C ++)
上次BC遇到一个大数题目,没有大数模板和不会使用JAVA的同学们GG了,赛后从队友哪里骗出大数模板.2333333,真的炒鸡nice(就是有点长),贴出来分享一下好辣. //可以处理字符串前导零 #i ...
- Backbone学习记录(2)
创建一个集合 1)new Backbone.Collection()方式 var user=new Backbone.Model({'name':'susan'}); var list=new Bac ...
- RHEL 6.5---SVN服务实现过程
主机名 IP地址 master 192.168.30.130 slave 192.168.30.131 安装 [root@master ~]# yum install -y subversion h ...
- apache mod_alias模块功能介绍
我觉得mod_alias根mod_rewrite挺像的,都可以实现url的重写,而mod_alias可以实现简单的url重写的功能 ,而mod_rewrite可以实现比较复杂的重写.mod_alias ...
- 用NPOI从Excel到DataTable
NPOI功能强大,不用装Excel,就可以操作表格中数据----Excel.Sheet------>DataTable private IWorkbook workbook = null; pr ...