HDU 5816 Hearthstone (状压dp)
2016-08-10 09:34
267 查看
题目大概说有两种卡牌,使用A牌能从牌堆摸两张牌,使用B牌能对对方造成xi点伤害。在你的回合,你从牌堆摸一张牌,问能对对方造成p点及以上伤害的概率。
要求的其实就是能造成p点以上伤害的牌堆排列数/牌堆全排列数。
全排列而且总数为20,这种就该想到尝试用状压DP。。
dp[S]表示已经摸的牌的集合为S的可行排列方案数
对于一个状态S,我们能从这个集合中已经摸的牌知道还能摸几张,即A的数目 * 2 - A的数目 - B的数目 + 一开始能摸的一张牌。
考虑状态的转移,我是用我为人人实现的:从小到大枚举状态S,判断S是否合法,即S的方案数不为0且还能摸牌,然后通过S去更新S+i(i∉S)状态的值。另外如果S能造成的伤害已经大于等于p了,那就没必要去更新它能转移到的状态,因为还剩下的牌直接求全排列计算其贡献,这样也能避免重复计算。
最后就循环遍历各个合法的状态累加贡献,这个贡献就是dp值 * 还没摸的牌的全排列数。这样就求出能造成p点以上伤害的牌堆排列数,再和全排列数用GCD搞搞输出答案即可。
没想到排列,老是纠结于概率,然后就g了。。
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-9
#define maxn 1100010
#define MOD 1000000007
int n,m;
long long p,a[22];
long long dp[maxn],fac[22];
int cnt1,cnt2;
int ok(int x)
{
cnt1 = 0,cnt2 = 0;
for(int i = 0; i < n; i++)
if(x & (1<<i))
cnt1++;
for(int i = 0; i < m; i++)
if(x & (1<<(n+i)))
cnt2++;
return cnt1 - cnt2 + 1 >= 0;
}
long long get_sum(int x)
{
long long sum = 0;
for(int i = 0; i < m; i++)
if(x&(1<<(n+i)))
sum += a[i];
return sum;
}
long long gcd(long long a,long long b)
{
if(b == 0)
return a;
return gcd(b,a%b);
}
int main()
{
int t,C = 1;
scanf("%d",&t);
fac[0] = 1;
for(int i = 1; i < 22; i++)
fac[i] = fac[i-1] * i;
while(t--)
{
scanf("%lld%d%d",&p,&n,&m);
memset(dp,0,sizeof(dp));
long long ans = 0;
for(int i = 0; i < m; i++)
scanf("%lld",&a[i]);
dp[0] = 1;
for(int i = 0; i < 1<<(n+m); i++)
{
if(!dp[i] || !ok(i))
continue;
if(get_sum(i) >= p)
{
ans += dp[i] * fac[n+m-cnt1-cnt2];
continue;
}
if(cnt1-cnt2+1 == 0)
continue;
for(int j = 0; j < n+m; j++)
if(!(i&(1<<j)))
dp[i|(1<<j)] += dp[i];
}
long long k = gcd(ans,fac[n+m]);
printf("%lld/%lld\n",ans/k,fac[n+m]/k);
}
return 0;
}
要求的其实就是能造成p点以上伤害的牌堆排列数/牌堆全排列数。
全排列而且总数为20,这种就该想到尝试用状压DP。。
dp[S]表示已经摸的牌的集合为S的可行排列方案数
对于一个状态S,我们能从这个集合中已经摸的牌知道还能摸几张,即A的数目 * 2 - A的数目 - B的数目 + 一开始能摸的一张牌。
考虑状态的转移,我是用我为人人实现的:从小到大枚举状态S,判断S是否合法,即S的方案数不为0且还能摸牌,然后通过S去更新S+i(i∉S)状态的值。另外如果S能造成的伤害已经大于等于p了,那就没必要去更新它能转移到的状态,因为还剩下的牌直接求全排列计算其贡献,这样也能避免重复计算。
最后就循环遍历各个合法的状态累加贡献,这个贡献就是dp值 * 还没摸的牌的全排列数。这样就求出能造成p点以上伤害的牌堆排列数,再和全排列数用GCD搞搞输出答案即可。
没想到排列,老是纠结于概率,然后就g了。。
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-9
#define maxn 1100010
#define MOD 1000000007
int n,m;
long long p,a[22];
long long dp[maxn],fac[22];
int cnt1,cnt2;
int ok(int x)
{
cnt1 = 0,cnt2 = 0;
for(int i = 0; i < n; i++)
if(x & (1<<i))
cnt1++;
for(int i = 0; i < m; i++)
if(x & (1<<(n+i)))
cnt2++;
return cnt1 - cnt2 + 1 >= 0;
}
long long get_sum(int x)
{
long long sum = 0;
for(int i = 0; i < m; i++)
if(x&(1<<(n+i)))
sum += a[i];
return sum;
}
long long gcd(long long a,long long b)
{
if(b == 0)
return a;
return gcd(b,a%b);
}
int main()
{
int t,C = 1;
scanf("%d",&t);
fac[0] = 1;
for(int i = 1; i < 22; i++)
fac[i] = fac[i-1] * i;
while(t--)
{
scanf("%lld%d%d",&p,&n,&m);
memset(dp,0,sizeof(dp));
long long ans = 0;
for(int i = 0; i < m; i++)
scanf("%lld",&a[i]);
dp[0] = 1;
for(int i = 0; i < 1<<(n+m); i++)
{
if(!dp[i] || !ok(i))
continue;
if(get_sum(i) >= p)
{
ans += dp[i] * fac[n+m-cnt1-cnt2];
continue;
}
if(cnt1-cnt2+1 == 0)
continue;
for(int j = 0; j < n+m; j++)
if(!(i&(1<<j)))
dp[i|(1<<j)] += dp[i];
}
long long k = gcd(ans,fac[n+m]);
printf("%lld/%lld\n",ans/k,fac[n+m]/k);
}
return 0;
}
相关文章推荐
- hdu 5816 Hearthstone (状压dp)
- HDU 5816 Hearthstone (状压dp)
- HDU 5816 Hearthstone (状压DP)
- HDU 5816 Hearthstone(状压DP)
- HDU 5816 Hearthstone(状态压缩DP+概率)
- HDU 5816 Hearthstone 概率dp
- HDU 5816 Hearthstone (Probability dp, Conbinations)
- hdu 5816 Hearthstone 状态dp
- hdu 5816 Hearthstone(状态压缩dp)
- Hdu-5816 Hearthstone(状态压缩DP)
- hdu 4539(状压dp)
- hdu 2686(状压dp)
- HDU 1992Tiling a Grid With Dominoes(状压dp)
- hdu 2686(状压dp)
- hdu 4539(状压dp)
- hdu 2167(状压dp)
- Travelling - HDU 3001 状压dp
- hdu 4539 郑厂长系列故事——排兵布阵 (状压DP)
- HDU 4084 Campus Design 状压dp
- hdu 4640(状压dp)