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.求出咒语机的最长上升序列 我们假设咒语机$ ...
随机推荐
- HDU1016【简单递归.DFS】
题意:一个环,相邻相加是素数. 思路: 直接深搜就好了.. output limit exceed 了好几发... 因为那个while里面的scanf前面的"~" 后来搜了outp ...
- HCNA网工笔记Day2 - IP编址
IP编址 主机地址子网掩码:区分主机位和网络位网络地址:主机位全部为0,比如 127.0.0.0/8广播地址:主机位全部为1,比如 127.255.255.255/8 一般网络地址和广播地址不能pin ...
- TCP协议 三次握手四次挥手
当某个应用端想基于TCP协议与另一个应用端通信时,它会发送一个通信请求. 这个请求必须被送到一个确切的地址.在双方“握手”之后,TCP 将在两个应用程序之间建立一个全双工 (full-duplex) ...
- wamp2.5版本配置多端口虚拟主机
1.保证httpd.conf下 LoadModule php5_module "D:/E/php/wamp/bin/php/php5.5.12/php5apache2_4.dll" ...
- Robot Framework操作mongodb数据库
RF对mongodb操作需要安装以下两个库: 1.pymongo 可以采用pip install pymongo: (如果下载过慢,可指定下载源,如:http:pypi.douban.com/simp ...
- UVA - 1349 D - Optimal Bus Route Design
4. D - Optimal Bus Route Design 题意:给出n(n<=100)个点的带权有向图,找出若干个有向圈,每个点恰好属于一个有向圈.要求权和尽量小. 注意即使(u,v)和( ...
- 1-16使用try-catch捕捉异常
处理异常 可以使用try-catch-处理异常,例如之前的程序可以使用try-catch-处理 package com.monkey1024.exception; import java.io.Fil ...
- solrJ的查询->统计【转】
package com.fjsh.SearchJobsFirst; import java.text.SimpleDateFormat; import java.util.Calendar; impo ...
- AJPFX总结面向对象中成员变量和成员方法的定义
//面向对象中成员变量和成员方法的定义格式:========================================= 成员变量定义在类中方法外,可以被该类中所有方法使用. ...
- Git之提交项目到远程github
1.在分支dev下,默认本地工作区有项目project 2. git add project [添加项目到暂存区] 3. git commit project -m "提交项目" ...