UVA 10531 Maze Statistics 迷宫统计 迷宫插头DP 四联通 概率
题意:
有一个N*M的图,每个格子有独立概率p变成障碍物。你要从迷宫左上角走到迷宫右下角。求每个格子成为一个有解迷宫中的障碍物的概率。N <= 5,M <= 6
分析:
这真是一道好题,网上几乎没有任何关于四连通的插头DP的任何资料,这道题目很好地反映了这类问题。
四连通中,只要你存在了右插头,必然存在下插头,当然,你的插头不一定需要真正连到可行格子中,因此在当前行中你只需要记录右插头。
但是不是需要换行的吗?由于下插头跟右插头是同时存在的,那么我们只需要把右插头当做下插头来用就可以了,是不是比普通的简单路径的插头dp简单很多?
来,我们看一下转移吧,其实情况的分类还是跟普通的插头dp一样的,不得不说cdq的插头普适性真是厉害。
1、同时存在左插头和上插头,那么只需要把连通块连一下,然后记一个右插头。
2、只存在左插头或者上插头,那么只需要延续连通块,再记一个右插头。
3、都不存在左插头和上插头,那么只需要新建一个连通块,记在右插头。
而换行操作,只需要把右插头变成下插头就可以了。
程序:(学习了某岛大神的代码之后才写出来了这份代码)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream> using namespace std; typedef long long LL;
const int HASH = ;
const int STATE = ;
const int MAXD = ;
const double EPS = 1e-;
int n, m;
double maze[MAXD][MAXD];
int code[MAXD], ch[MAXD]; int fcmp(double x)
{
return x < -EPS ? - : x > EPS;
} void decode(LL st)
{
int i;
for (i = m; i >= ; --i)
{
code[i] = st&;
st >>= ;
}
} LL encode()
{
LL st = ;
int i, cnt = ;
memset(ch, -, sizeof(ch));
ch[] = , ch[] = ;
for (i = ; i <= m; ++i)
{
if (ch[code[i]] == -)
ch[code[i]] = ++cnt;
code[i] = ch[code[i]];
st <<= ;
st |= code[i];
}
return st;
} struct HASHMAP
{
int head[HASH], next[STATE], size;
LL state[STATE];
double f[STATE];
void clear()
{
size = ;
memset(head, -, sizeof(head));
}
void push(LL st, double ans)
{
int i;
decode(st);
for (i = ; i <= m; ++i)
if (code[i] == )
break ;
if (i == m+) //如果编号为1的连通块不存在,那就不可行
return ;
int x = st%HASH;
for (i = head[x]; i != -; i = next[i])
if (st == state[i])
{
f[i] += ans;
return ;
}
f[size] = ans;
state[size] = st;
next[size] = head[x];
head[x] = size ++;
}
}hm[]; void in()
{
int i, j;
scanf("%d %d", &n, &m);
for (i = ; i <= n; ++i)
for (j = ; j <= m; ++j)
scanf("%lf", &maze[i][j]);
} void dp_blank(int i, int j, int cur)
{
if (!fcmp(1.0-maze[i][j]))
return ;
int k, lef, up, t;
for (k = ; k < hm[cur].size; ++k)
{
if (!fcmp(hm[cur].f[k]))
continue ;
decode(hm[cur].state[k]);
lef = code[j-], up = code[j];
if (lef && up)//分类讨论只对右插头做修改
{
if (lef != up)
{
if (lef < up)
swap(lef, up);
for (t = ; t <= m; ++t)
if (code[t] == lef)
code[t] = up;
}
}
else
{
if (lef || up)
code[j] = lef|up;
else
code[j] = m+;
}
hm[cur^].push(encode(), hm[cur].f[k]*(1.0-maze[i][j]));
}
} void dp_block(int i, int j, int cur)
{
if (!fcmp(maze[i][j]))
return ;
int k;
for (k = ; k < hm[cur].size; ++k)
{
if (!fcmp(hm[cur].f[k]))
continue ;
decode(hm[cur].state[k]);
code[j] = ;//同理
hm[cur^].push(encode(), hm[cur].f[k]*maze[i][j]);
}
} double solve()
{
int i, j, cur = ;
memset(code, , sizeof(code));
hm[cur].clear();
code[] = ;
hm[cur].push(encode(), );
for (i = ; i <= n; ++i)
for (j = ; j <= m; ++j)
{
hm[cur^].clear();
dp_blank(i, j, cur);
dp_block(i, j, cur);
cur ^= ;
}
double ret = ;
for (i = ; i < hm[cur].size; ++i)
{
decode(hm[cur].state[i]);
if (code[m] == )
ret += hm[cur].f[i];
}
return ret;
} void work()
{
int i, j;
double sum = solve();
for (i = ; i <= n; ++i)
for (j = ; j <= m; ++j)
{
double cache = maze[i][j];
maze[i][j] = 1.0;
printf("%.6lf%c", solve()*cache/sum, (j == m) ? '\n' : ' ');
maze[i][j] = cache;
}
} int main()
{
int T, iCase = ;
scanf("%d", &T);
while (T --)
{
if (iCase++)
printf("\n");
in();
work();
}
return ;
}
UVA 10531 Maze Statistics 迷宫统计 迷宫插头DP 四联通 概率的更多相关文章
- uva 11270 - Tiling Dominoes(插头dp)
题目链接:uva 11270 - Tiling Dominoes 题目大意:用1∗2木块将给出的n∗m大小的矩阵填满的方法总数. 解题思路:插头dp的裸题,dp[i][s]表示第i块位置.而且该位置相 ...
- BZOJ_3365_[Usaco2004 Feb]Distance Statistics 路程统计&&POJ_1741_Tree_点分治
BZOJ_3365_[Usaco2004 Feb]Distance Statistics 路程统计&&POJ_1741_Tree_点分治 Description 在得知了自己农 ...
- 动态规划之插头DP入门
基于联通性的状态压缩动态规划是一类非常典型的状态压缩动态规划问题,由于其压缩的本质并不像是普通的状态压缩动态规划那样用0或者1来表示未使用.使用两种状态,而是使用数字来表示类似插头的状态,因此.它又被 ...
- 插头dp
插头dp 感受: 我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案.而是方案本来就在那里,我们只是枚举状态统计了答案. 看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单 ...
- HDU 4113 Construct the Great Wall(插头dp)
好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm- ...
- HDU 4949 Light(插头dp、位运算)
比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题.. 因为写完之后,无限TLE... 直到后来用位运算代替了我插头dp常用的decode.encode.shift三个函数以 ...
- 插头DP专题
建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议 ...
- HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)
插头DP基础题的样子...输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物.输出哈密顿回路(可以多回路)方案数... 看了个ppt,画了下图...感觉还是挺有效的... 参考 ...
- 插头dp的几个模板
/* ural1519 求经过全部可行点的哈密顿回路的个数 括号匹配法,转移有点复杂,可是时间空间比較小 */ #include<cstdio> #include<cstring&g ...
随机推荐
- 数组中的each 和 jquery 中的 each
数组的实例上都有一个叫做 forEach 的方法,这个方法定义在 Array.prototype 上,所以数组的所有实例都可以使用 forEach 这个方法. forEach 方法的语法结构如下: v ...
- 树的直径(两个bfs)
题目链接:https://cn.vjudge.net/problem/POJ-2631 树的直径:树中的最长链 具体思路:随便找一个点bfs,然后找到最长的链,然后再以找到的点作为起点进行bfs,然后 ...
- 用CRF做命名实体识别(一)
用CRF做命名实体识别(二) 用CRF做命名实体识别(三) 用BILSTM-CRF做命名实体识别 博客园的markdown格式可能不太方便看,也欢迎大家去我的简书里看 摘要 本文主要讲述了关于人民日报 ...
- PHP深浅拷贝
举个栗子: <?php class Example1 { public $name; public function __construct($name) { $this->name = ...
- python基础之上下文管理器
前言 关于计算器运行的上下文的概念,我的理解也不是很深:按我的理解就是程序在运行之前,其所需要的资源,运行环境等都会被序列化,然后加入到CPU的任务队列中,等待调度系统分配时间片执行.下面谈谈pyth ...
- LCD常用接口原理【转】
转自:http://blog.csdn.net/wocao1226/article/details/23870149 LCD常用接口原理 点击打开链接 点击打开链接 点击打开链接 点击打开链接 点击打 ...
- 做Mysql主从时,注意使用replicate_wild_do_table和replicate-wild-ignore-table【转】
做Mysql主从时,注意使用replicate_wild_do_table和replicate-wild-ignore-table 浓缩版: 使用replicate_do_db和replicate_i ...
- Linux轻量级自动运维工具-Ansible浅析【转】
转自 Linux轻量级自动运维工具-Ansible浅析 - ~微风~ - 51CTO技术博客http://weiweidefeng.blog.51cto.com/1957995/1895261 Ans ...
- Deep Learning基础--各个损失函数的总结与比较
损失函数(loss function)是用来估量你模型的预测值f(x)与真实值Y的不一致程度,它是一个非负实值函数,通常使用L(Y, f(x))来表示,损失函数越小,模型的鲁棒性就越好.损失函数是经验 ...
- [前端随笔][css] 弹性布局
说在前面 弹性布局,顾名思义就是有弹性,能够根据屏幕/当前空间大小自由伸缩的.使用弹性布局可以很好的适应各种尺寸的客户端. 关键代码 display:flex; 设定元素为弹性布局 <文档传送门 ...