题意:

    有一个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 ;
}

最新文章

  1. (转载)开始iOS 7中自动布局教程(一)
  2. 利用Python【Orange】结合DNA序列进行人种预测
  3. nginx负载均衡集群中的session共享说明
  4. [BZOJ3672][UOJ#7][NOI2014]购票
  5. Python 2.7.9 Demo - JSON的编码、解码
  6. 读取Cookie及Cookie所有属性操作方法
  7. 【英语】Bingo口语笔记(72) - play系列
  8. Yii连接多个数据库的方法
  9. 使用java远程调试技术监控代码运行
  10. shell编程的一些例子2
  11. 利用AWS简单存储服务(S3)托管网站
  12. python 3Des 加密
  13. MongoDB 3.4版本, C# 驱动 2.4 操作
  14. 百度SMS SDK for .Net
  15. ExecutorService
  16. SpringBoot 请求参数后端校验
  17. 使用控制台对Redis执行增删改查命令
  18. spring资源加载结构解析
  19. Latex一次添加两个图(并列),半栏
  20. Window上,启动Tomcat服务之后,关闭启动窗口,服务器也随之关闭

热门文章

  1. layui结合mybatis的pagehelper插件的分页通用的方法
  2. 高性能优秀的服务框架-dubbo介绍
  3. 剑指offer算法题
  4. ie7浏览器兼容问题
  5. 在Linux上安装pycharm
  6. tftp 开发板ping不通PC机
  7. 关于VS2010的一些操作
  8. STL中stack/queue/map以及Boost unordered_map 的使用方法
  9. MVC 之AjaxHelper
  10. 关于Web2.0概念的一篇小短文