[ACM] hihoCoder 1075 开锁魔法III (动态规划,组合数学)

2024-01-28 13:08

本文主要是介绍[ACM] hihoCoder 1075 开锁魔法III (动态规划,组合数学),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

描述

一日,崔克茜来到小马镇表演魔法。

其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它。初始时,崔克茜将会随机地选择 k 个盒子用魔法将它们打开。崔克茜想知道最后所有盒子都被打开的概率,你能帮助她回答这个问题吗?

输入

第一行一个整数 T (T ≤ 100)表示数据组数。 对于每组数据,第一行有两个整数 n 和 k (1 ≤ n ≤ 300, 0 ≤ k ≤ n)。 第二行有 n 个整数 ai,表示第 i 个盒子中,装有可以打开第 ai 个盒子的钥匙。

输出

对于每组询问,输出一行表示对应的答案。要求相对误差不超过四位小数。

样例输入
4
5 1
2 5 4 3 1
5 2
2 5 4 3 1
5 3
2 5 4 3 1
5 4
2 5 4 3 1
样例输出
0.000000000
0.600000000
0.900000000
1.000000000


解题思路:

做这种题真是头疼,没思路无从下手,还是做的题少再加上没有仔细得分析,下面就来好好得分析一下本题。

1.只要打开一个盒子,那么就能打开一连串的盒子,因为打开的盒子里面有着另一个盒子的钥匙。

2.要求全部盒子打开的概率,那么用 全部盒子可以打开的方法除以总的方法数就可以了。 总的方法数好求,就是C(n,k),即在n个盒子中选择k个打开。

对于第一点,打开一连串的盒子,那么最后一个盒子里的钥匙一定是第一个打开盒子的钥匙,比如首先打开2号盒子,那么陆续打开x个盒子以后,

突然发现最后打开的那个盒子里面是2号盒子的钥匙,那么这一连串打开就结束了,也就构成了一个循环。所以很容易联想到组合数学中置换群的循

环节。所以就把所有的循环节都找出来。比如一共有num个循环节,那么能打开全部盒子的条件为给定的钥匙个数k一定要大于等于num,否则最少

有一个循环节里不能打开一个盒子。

对于第二点,求可以打开的方法数采用动态规划递推的方法。首先划分阶段,前面说到一共有num个循环节,那么就把整个问题划分为num个阶

段,每次循环解决一个阶段的问题。接下来是定义状态 ,用dp[ i ] [ j ] ,表示用j把钥匙打开前i个阶段的盒子一共有多少种方法。用到乘法原理,乘

法远离是第一个阶段有a种选择,第二个阶段有b种选择,那么前两个阶段一共有a*b种选择。这里要用到。用递推就要考虑状态的转移:比如当前是

dp [ i ] [ j] , 那么前一个状态为 dp [ i -1] ] [ j - use ]  , 即用j-use把钥匙打开前i-1个阶段的盒子一共有的方法数,也就是说打开第i个阶段的盒子用了

use个钥匙,一共有  C(cnt,use)种方法,cnt为第i个阶段一共有cnt个待打开的盒子。那么根据乘法原理 dp[i][j]=dp[i-1][j-use]*C(cnt,use) ,但是前一

个状态不唯一,也就是use的值可以变化,所以要方法累加 即dp[i][j]+=dp[i-1][j-use]*C(cnt,use).  这里dp[i][j]是未知的,要求它就必须知道dp[i-1][j-

use],也就是用已知的状态去推出未知的状态。

这里用一个例子具体说明一下: 比如一共可以划分为2个阶段,第一个阶段有2个盒子,第二阶段有3个盒子,而给定的钥匙数k为3.

初始dp [ 0 ] [ 0 ] =1;

dp[1][1]+=dp[0][0]*c[2][1] , 第一个阶段里有2个盒子,只用一把钥匙,任选一个盒子开开,一共有c[2][1]种方法

dp[1][2]+=dp[0][0]*c[2][2],  用两把钥匙,只有一种方法

阶段1已求完,用已知的数据去推出未知

dp[2][2]+=dp[1][1]*c[3][1],   乘法原理,第一阶段用了1把钥匙,第二阶段使用一把钥匙,去开第二阶段的3个盒子,任选1个 C[3][1]种方法

dp[2][3]+=dp[1][1]*c[3][2];                                                                  第二阶段使用两把钥匙,去开第二阶段的3个盒子,任选2个C[3][2]种方法

dp[2][3]+=dp[1][2]*c[3][1];                        第一阶段用了2把钥匙,第二阶段使用一把钥匙,去开第二阶段的3个盒子,任选1个C[3][1]种方法


到此本题就分析完了。

值得特别注意的是,最后要用可行方法数除以总的方法数,考虑到精度问题,分子为dp [cnt ][k] 设置为double, 分母组合数也设置成double ,很容易出错。


代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <iomanip>
using namespace std;
int n,k;
int match[320];
double c[320][320];
bool vis[320];
double dp[320][320];
vector<int>loop;void getComb()
{for(int i=0;i<=300;i++){c[i][0]=c[i][i]=1.0;for(int j=1;j<i;j++)c[i][j]=c[i-1][j]+c[i-1][j-1];}
}int main()
{getComb();int t;cin>>t;while(t--){cin>>n>>k;for(int i=1;i<=n;i++)cin>>match[i];loop.clear();memset(vis,0,sizeof(vis));for(int i=1;i<=n;i++)//求循环节{if(vis[i])continue;int cnt=0,cur=i;while(!vis[cur]){cnt++;vis[cur]=1;cur=match[cur];}loop.push_back(cnt);}int num=loop.size();if(k<num){printf("%.9lf\n",0.0);continue;}memset(dp,0,sizeof(dp));dp[0][0]=1.0;for(int i=0;i<num;i++){for(int j=0;j<k;j++){if(dp[i][j]==0)continue;for(int use=1;use<=loop[i]&&j+use<=k;use++)dp[i+1][j+use]+=dp[i][j]*c[loop[i]][use];//阶段相乘,前两个阶段的方法数等于第一阶段的方法数*第二阶段的方法数}}printf("%.9lf\n",dp[num][k]/c[n][k]);}
}




这篇关于[ACM] hihoCoder 1075 开锁魔法III (动态规划,组合数学)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/653720

相关文章

C#如何动态创建Label,及动态label事件

《C#如何动态创建Label,及动态label事件》:本文主要介绍C#如何动态创建Label,及动态label事件,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#如何动态创建Label,及动态label事件第一点:switch中的生成我们的label事件接着,

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

mybatis-plus 实现查询表名动态修改的示例代码

《mybatis-plus实现查询表名动态修改的示例代码》通过MyBatis-Plus实现表名的动态替换,根据配置或入参选择不同的表,本文主要介绍了mybatis-plus实现查询表名动态修改的示... 目录实现数据库初始化依赖包配置读取类设置 myBATis-plus 插件测试通过 mybatis-plu

基于Canvas的Html5多时区动态时钟实战代码

《基于Canvas的Html5多时区动态时钟实战代码》:本文主要介绍了如何使用Canvas在HTML5上实现一个多时区动态时钟的web展示,通过Canvas的API,可以绘制出6个不同城市的时钟,并且这些时钟可以动态转动,每个时钟上都会标注出对应的24小时制时间,详细内容请阅读本文,希望能对你有所帮助...

Vue中动态权限到按钮的完整实现方案详解

《Vue中动态权限到按钮的完整实现方案详解》这篇文章主要为大家详细介绍了Vue如何在现有方案的基础上加入对路由的增、删、改、查权限控制,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、数据库设计扩展1.1 修改路由表(routes)1.2 修改角色与路由权限表(role_routes)二、后端接口设计

前端 CSS 动态设置样式::class、:style 等技巧(推荐)

《前端CSS动态设置样式::class、:style等技巧(推荐)》:本文主要介绍了Vue.js中动态绑定类名和内联样式的两种方法:对象语法和数组语法,通过对象语法,可以根据条件动态切换类名或样式;通过数组语法,可以同时绑定多个类名或样式,此外,还可以结合计算属性来生成复杂的类名或样式对象,详细内容请阅读本文,希望能对你有所帮助...

Rust中的Drop特性之解读自动化资源清理的魔法

《Rust中的Drop特性之解读自动化资源清理的魔法》Rust通过Drop特性实现了自动清理机制,确保资源在对象超出作用域时自动释放,避免了手动管理资源时可能出现的内存泄漏或双重释放问题,智能指针如B... 目录自动清理机制:Rust 的析构函数提前释放资源:std::mem::drop android的妙

Nginx实现动态封禁IP的步骤指南

《Nginx实现动态封禁IP的步骤指南》在日常的生产环境中,网站可能会遭遇恶意请求、DDoS攻击或其他有害的访问行为,为了应对这些情况,动态封禁IP是一项十分重要的安全策略,本篇博客将介绍如何通过NG... 目录1、简述2、实现方式3、使用 fail2ban 动态封禁3.1 安装 fail2ban3.2 配

Vue3中的动态组件详解

《Vue3中的动态组件详解》本文介绍了Vue3中的动态组件,通过`component:is=动态组件名或组件对象/component`来实现根据条件动态渲染不同的组件,此外,还提到了使用`markRa... 目录vue3动态组件动态组件的基本使用第一种写法第二种写法性能优化解决方法总结Vue3动态组件动态