一道很有意思的状压DP
如果硬做 这题的空间是 (O(2^{40})) 时间是 (O(2^{40} * n^2))
有n个人去执行n个任务,每个人执行每个任务有不同的成功率,每个人只能执行一个任务,求所有任务都执行的总的成功率。
很显然这题 求的是最大的概率 每个人都不一样…肯定是状压(费用流)
考虑怎么压一维做…因为这个是一一对应的 会少掉很多状态
比如
1->2
2->3
3->1
和
2->3
1->2
3->1
是一样的 这样就可以按人
来状压了…
所以 (dp_i) 表示 做过任务的人
然后你可以根据 二进制位为1的个数推断出现在在选第几个物品…然后枚举当前未选过物品的人进行转移
#include<bits/stdc++.h>
using namespace std ;
#define int long long
#define fi first
#define se second
#define pb push_back
inline int read() {
register int x = 0 , f = 1 ;
register char c = getchar() ;
for( ; ! isdigit(c) ; c = getchar()) if(c == '-') f = -1 ;
for( ; isdigit(c) ; c = getchar()) x = (x << 1) + (x << 3) + (c & 15) ;
return x * f ;
}
template < typename T > inline bool cmax(T & x , T y) {
return x < y ? (x = y) , 1 : 0 ;
}
template < typename T > inline bool cmin(T & x , T y) {
return x > y ? (x = y) , 1 : 0 ;
}
template < typename T > inline bool cabs(T & x) {
return x > 0 ? 1 : (x = - x) , 0 ;
}
inline int QP(int x , int y , int Mod) {
int ans = 1 ;
for( ; y ; y >>= 1 , x = (x * x) % Mod)
if(y & 1) ans = (ans * x) % Mod ;
return ans ;
}
int n ;
const int N = 21 ;
double dp[1 << N] ;
double c[N][N] ;
signed main() {
n = read() ;
for(register int i = 1 ; i <= n ; i ++)
for(register int j = 1 ; j <= n ; j ++) c[i][j] = read() ;
for(register int i = 1 ; i <= n ; i ++)
for(register int j = 1 ; j <= n ; j ++) c[i][j] /= 100 ;
int s = (1 << n) - 1 ;
dp[0] = 1.00 ;
for(register int i = 0 ; i <= s ; i ++) {
int cnt = 1 ;
int t = i ; while(t) t -= (t & -t) , cnt ++ ;
for(register int j = 1 ; j <= n ; j ++)
if(! (i & (1 << j - 1))) cmax(dp[i | (1 << j - 1)] , 1.0 * dp[i] * c[j][cnt]) ;
}
printf("%.9f" , dp[s] * 100) ;
return 0 ;
}