「CF678E Another Sith Tournament」题解

Description


如果你做过 CF16ECF540D 的话,那么恭喜你,这道题你 A 了。
因为1是一定在最后胜利的,所以我们考虑倒推,又因为 \(n\leq18\),容易想到状压。
\(dp[i]({i|i\in [1,2^n-1]})\) 表示目前存活的人的集合为 i 时,1获胜的概率,\(p[i][j]\) 表示 \(i\) 战胜 \(j\) 的概率。
易知 \(dp[1]=1\)
这个思路和 CF540D 类似。
因为我们需要保证1能存活到最后且是从1存活开始转移,所以当前方案的概率仅有可能会从死掉1个人后1存活的概率转移来。
这个过程其实是记忆化搜索,只不过我们把它写成了 \(\operatorname{DP}\) 的形式。
那么转移方程就清晰了

\[dp[i]=\max(dp[i-2^k]\times p[j][k],dp[i-2^j]\times p[k][j]) \]

为什么比最大值呢?因为1可以操作比赛,故而会选择使得自己胜率最高的比赛顺序。
Code:

#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN=25;
const int MAXM=3e5+5;
int n;
double p[MAXN][MAXN];
double dp[MAXM];
int main(){
	scanf("%d",&n);
	for (int i=0;i<n;i++){
		for (int j=0;j<n;j++){
			scanf("%lf",&p[i][j]);
		}
	}
	int k=(1<<n);
	dp[1]=1; 
	for (int kk=2;kk<k;kk++){
		for (int i=0;i<n;i++){
			if(!(kk&(1<<i)))
			{
				continue;
			}
			for (int j=i+1;j<n;j++){
				if (!(kk&(1<<j)))
				{
					continue;
				}
				dp[kk]=max(dp[kk],dp[kk^(1<<i)]*p[j][i]+dp[kk^(1<<j)]*p[i][j]);
			}
		}
	}
	printf("%.7lf\n",dp[k-1]);
	return 0;
}

\(\Bbb{End.}\)
\(\Bbb{Thanks} \space \Bbb{For} \space \Bbb{Reading.}\)

posted @ 2021-09-29 21:53  StranGePants  阅读(75)  评论(0编辑  收藏  举报