DTOJ #3124. 开锁(unlock)

【题目描述】

A君有 $n$ 个盒子,每个盒子被一把锁锁着,每个盒子内都有一把钥匙。对于每个盒子而言有且仅有一把钥匙能打开锁着它的锁,而打开它后便能拿着放置在这个盒子内的钥匙去开启其他盒子。

现在A君打算随机选择 $k$ 个盒子并用魔法将它们打开,并用所得到的钥匙去尝试开启其他所有的盒子 $f$ 开启一个盒子后,新得到的钥匙还能继续尝试使用)。

A君想知道,最终他能打开所有盒子的概率是多少,请你帮助他。

【输入格式】

第一行一个整数 $T$ 表示数据组数。

每组数据第一行两个整数 $n,k$,意义见题目描述。

第二行 $n$ 个整数 $a_i$,表示第i个盒子中装有可以打开第 $a_i$ 个盒子的锁的钥匙。

【输出格式】

对于每组数据输出一行表示答案,要求绝对误差不超过4位小数。

【样例】

样例输入
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

【数据范围与提示】

【题解】

又是一道玄学+信仰题。。。

推概率 $dp$ 推了好久没推出来,直接写了个算方案数的,用 $long double$ 存就过了。。。

由于对精度要求不高,所以只用 $long double$ 存前几位有效位的正确性是有保证的。。。

观察题目,不难发现题意就是给你 $n$ 个点构成许多环,求随机选 $k$ 个点覆盖所有环的概率。

考虑 $dp$,设 $f[i][j]$ 表示前 $i$ 个环总共选了 $j$ 个数的方案数,转移时枚举当前环选了几个数,乘上组合数即可。

分析一下效率,第一层循环枚举环和第三层循环枚举选了几个数合在一起是 $O(n)$ 的,因为环的大小总和为 $n$,而第二层循环是 $O(k)$ 的,总复杂度 $O(Tnk)$。

【代码】

#include<bits/stdc++.h>
inline int read ( void )
{
	int x=0;char ch;bool f=true;
	while ( !isdigit(ch=getchar()) ) if ( ch=='-' ) f=false;
	for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
	return f ? x : -x ;
}
int n,k,A[310];bool vis[310];
long double C[310][310],f[310][310];
std::vector<int> B;
signed main()
{
	for ( int i=0;i<=300;i++ )
	{
		C[i][0]=1;
		for ( int j=1;j<=i;j++ ) C[i][j]=C[i-1][j]+C[i-1][j-1];
	}
	for ( int T=read();T--; )
	{
		for ( int i=1;i<=n;i++ ) vis[i]=false;
		B.clear();memset(f,0,sizeof(f));
		n=read();k=read();
		for ( int i=1;i<=n;i++ ) A[i]=read();
		for ( int i=1;i<=n;i++ ) if ( !vis[i] )
		{
			int cnt=1;vis[i]=true;
			for ( int x=A[i];x!=i;x=A[x] ) cnt++,vis[x]=true;
			B.push_back(cnt);
		}
		if ( k<(int)B.size() ) { puts("0.000000000");continue; }
		std::sort(B.begin(),B.end());
		if ( k>=n-B[0]+1 ) { puts("1.000000000");continue; }
		f[0][0]=1;
		for ( int i=1;i<=(int)B.size();i++ ) for ( int j=i;j<=k;j++ ) for ( int x=1;x<=j-i+1 and x<=B[i-1];x++ ) f[i][j]+=f[i-1][j-x]*C[B[i-1]][x];
		printf("%.9LF\n",f[(int)B.size()][k]/C[n][k]);
	}
}

  

转载于:https://www.cnblogs.com/RenSheYu/p/11318361.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您想了解如何使用uni.writeblecharacteristicvalue传递2进制开锁的命令。 首先,您需要了解您要控制的设备的通信协议和命令格式。然后,您需要在您的应用程序中创建一个写特征值的BLE特征对象,并将其连接到您的设备。接下来,您可以使用`uni.writeblecharacteristicvalue`方法向设备发送2进制开锁命令。 以下是一个示例代码片段,展示如何使用uni.writeblecharacteristicvalue方法发送2进制开锁命令: ``` // 创建写特征对象 const writeCharacteristic = uni.createBLEWriteCharacteristic({ // 这里填写您的设备特征值UUID characteristicId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', // 这里填写您的设备服务UUID serviceId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', // 这里填写您要发送的2进制命令 value: ArrayBuffer.from([0x01, 0x02, 0x03, 0x04]) }) // 向设备发送2进制命令 uni.writeBLECharacteristicValue({ // 这里填写您的设备ID deviceId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 这里填写您的设备服务UUID serviceId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', // 这里填写您的设备特征值UUID characteristicId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', // 这里填写您的写特征对象 value: writeCharacteristic.value, success(res) { console.log('写入成功', res) }, fail(err) { console.log('写入失败', err) } }) ``` 请注意,这只是一个示例代码片段,您需要根据您的实际情况进行适当的更改和调整。同时,请确保您的设备支持BLE通信,并且在与设备进行通信之前已经成功建立了连接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值