题意:

    有一个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 四联通 概率的更多相关文章

  1. uva 11270 - Tiling Dominoes(插头dp)

    题目链接:uva 11270 - Tiling Dominoes 题目大意:用1∗2木块将给出的n∗m大小的矩阵填满的方法总数. 解题思路:插头dp的裸题,dp[i][s]表示第i块位置.而且该位置相 ...

  2. BZOJ_3365_[Usaco2004 Feb]Distance Statistics 路程统计&&POJ_1741_Tree_点分治

    BZOJ_3365_[Usaco2004 Feb]Distance Statistics 路程统计&&POJ_1741_Tree_点分治 Description     在得知了自己农 ...

  3. 动态规划之插头DP入门

    基于联通性的状态压缩动态规划是一类非常典型的状态压缩动态规划问题,由于其压缩的本质并不像是普通的状态压缩动态规划那样用0或者1来表示未使用.使用两种状态,而是使用数字来表示类似插头的状态,因此.它又被 ...

  4. 插头dp

    插头dp 感受: 我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案.而是方案本来就在那里,我们只是枚举状态统计了答案. 看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单 ...

  5. HDU 4113 Construct the Great Wall(插头dp)

    好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm- ...

  6. HDU 4949 Light(插头dp、位运算)

    比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题.. 因为写完之后,无限TLE... 直到后来用位运算代替了我插头dp常用的decode.encode.shift三个函数以 ...

  7. 插头DP专题

    建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议 ...

  8. HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)

    插头DP基础题的样子...输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物.输出哈密顿回路(可以多回路)方案数... 看了个ppt,画了下图...感觉还是挺有效的... 参考 ...

  9. 插头dp的几个模板

    /* ural1519 求经过全部可行点的哈密顿回路的个数 括号匹配法,转移有点复杂,可是时间空间比較小 */ #include<cstdio> #include<cstring&g ...

随机推荐

  1. 回溯算法_01背包问题_Java实现

    原文地址:http://blog.csdn.net/ljmingcom304/article/details/50314839 本文出自:[梁敬明的博客] 1.回溯算法 回溯算法也叫试探法,通俗的将就 ...

  2. BAT-快速切换JDK1.6/1.7

    Code: @echo OFF rem 修改背景/字体颜色为黑/绿 color 0A rem 自动设置JDK环境变量,本程序不会对 [系统环境变量] 造成破环!不需要重启!! rem 没有%%JAVA ...

  3. Arm-kernel 内存收集【转】

    转自:http://blog.csdn.net/linyt/article/details/6627664 Linux kernel的内存管理子系统非常复杂,为了深入了解内存管理系统,我打算分多篇文章 ...

  4. queue_delayed_work和queue_work区别 (转http://blog.csdn.net/dosculler/article/details/7968101)

    queue_delayed_work和queue_work 一.参考文献: 1)http://www.linuxidc.com/Linux/2011-08/41655.htm queue_delaye ...

  5. Python 类的名称空间和组合

    一.Python类的名称空间 class Student(object): School = '北京大学' def __init__(self): pass stu1 = Student() stu1 ...

  6. Bootstrap的使用。。。

    概览 深入了解 Bootstrap 底层结构的关键部分,包括我们让 web 开发变得更好.更快.更强壮的最佳实践. HTML5 文档类型 Bootstrap 使用到的某些 HTML 元素和 CSS 属 ...

  7. Delphi 绘图对象

    来自:http://blog.csdn.net/lailai186/article/details/8755430 ========================================== ...

  8. django rest_framework比较完整的自定义实现样例

    里面有自定义的更新策略, 序列化时,考虑nest及显示. 很有参考意义. 然后,前端,可以考虑用angular.js或vue.js实现. 每次以token进行认证. url.py router = D ...

  9. yii2联表查询

    我们用实例来说明这一部分 表结构 现在有客户表.订单表.图书表.作者表, 客户表Customer   (id  customer_name) 订单表Order          (id  order_ ...

  10. [实战]MVC5+EF6+MySql企业网盘实战(16)——逻辑重构3

    写在前面 本篇文章将新建文件夹的逻辑也进行一下修改. 系列文章 [EF]vs15+ef6+mysql code first方式 [实战]MVC5+EF6+MySql企业网盘实战(1) [实战]MVC5 ...