题意:

    有一个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. 常用的css3新特性总结

    1:CSS3阴影 box-shadow的使用和技巧总结: 基本语法是{box-shadow:[inset] x-offset y-offset blur-radius spread-radiuscol ...

  2. 二进制、十进制、十六进制(python)

    int(“x”,base=2/8/16)是把x都转换成十进制 二进制: 1111=1*2的3次方+1*2的2次方+1*2的1次方+1*2的0次方  =8+4+2+1=15 1000=1*2的3次方+0 ...

  3. 消息队列ActiveMQ的使用详解

    通过上一篇文章 <消息队列深入解析>,我们已经消息队列是什么.使用消息队列的好处以及常见消息队列的简单介绍. 这一篇文章,主要带大家详细了解一下消息队列ActiveMQ的使用. 学习消息队 ...

  4. ECNA 2017

    ECNA 2017 Abstract Art 题目描述:求\(n\)个多边形的面积并. solution 据说有模板. Craters 题目描述:给定\(n\)个圆,求凸包的周长. solution ...

  5. opencv配置过程 (cmake,vs2013,qt 5.4)

    平台及软件: Windows 7 X86 Visual Studio 2013 OpenCV3.0.0 Cmake3.3 1.下载Windows下的安装文件OpenCV-3.0.0.exe,解压,选择 ...

  6. 转:google测试分享-问题和挑战

    原文: http://blog.sina.com.cn/s/blog_6cf812be0102vxer.html 前言:这个系列分享的内容大部分都是出自于<google是如何测试的>的书, ...

  7. 端口扫描———nmap

    nmap教程之nmap命令使用示例(nmap使用方法) 浏览:8268 | 更新:2014-03-29 17:23 Nmap是一款网络扫描和主机检测的非常有用的工具.Nmap是不局限于仅仅收集信息和枚 ...

  8. shell之read命令

    一.概述 read命令接收标准输入(键盘)的输入,或者其他文件描述符的输入.得到输入后,read命令将数据放入一个标准变量中. 二.使用举例(这里仅列出一些常用的选项) 1.基本读取 #!/bin/b ...

  9. 4.rabbitmq 路由

    1. 生产者 #coding:utf8 import pika import json import sys severity = sys.argv[1] if len(sys.argv) > ...

  10. 湖南省第十一届大学生程序设计竞赛:Internet of Lights and Switches(HASH+二分+异或前缀和)

    Internet of Lights and Switches Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 3  Solved: 3[Submit][ ...