裁玻璃
#include<bits/stdc++.h>
#define intn long long
using namespace std;
int dp[1100][1100],a[1100],s[1100];
int getsum(int s)
{
int res=0;
while(s)
{
if(s&1)
res++;
s>>=1;
}
return res;
}
int judge1(int s1,int sd)
{
if((s1&sd)||(s1<<1)&sd)
return 1;
else
return 0;
}
int judge2(int s1,int s2)
{
if((s1&s2)||((s1<<1)&s2)||(s1&(s2<<1)))
return 1;
else
return 0;
}
main(void)
{
int t;
int n,m,x;
cin>>t;
for(int z=1;z<=t;z++)
{
cin>>n>>m;
memset(dp, 0, sizeof(dp));
memset(a, 0, sizeof(a));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>x;
a[i]<<=1;
a[i]+=!x;
}
}
for(int i=0;i<(1<<m-1);i++)
{
s[i]=getsum(i);
}
for(int i=2;i<=n;i++)
{
for(int p=0;p<(1<<m-1);p++)
{
if(judge1(p,a[i-1])||(p&(p<<1)))continue;
for(int q=0;q<(1<<m-1);q++)
{
if(q&(q<<1)||judge1(q,a[i])||judge1(q,a[i-1])||judge2(p,q))continue;
dp[i][q]=max(dp[i][q],dp[i-1][p]+s[q]);
}
}
}
int ans=0;
for(int i=0;i<(1<<m-1);i++)
{
ans=max(ans,dp[n][i]);
}
printf("%d\n",ans);
}
}
炮兵阵地
#include<iostream>
using namespace std;
int n,m,ans,dp[(1<<10)][(1<<10)][13]/*滚动数组*/,a[115],Sum[(1<<10)];
char x;
int getsum(int S) //当前状态 S 里面包含几个 1
{
int tot=0;
while(S) {if(S&1) tot++; S>>=1;}
return tot;
}
int main()
{
cin>>n>>m;
for(int i=2;i<n+2;i++)
for(int j=2;j<m+2;j++)
cin>>x,a[i]<<=1,a[i]+=(x=='H'?1:0); //转成二进制数
for(int i=0;i<(1<<m);i++)
Sum[i]=getsum(i); //初始化 Sum 数组
for(int i=0+2;i<n+2;i++)
for(int L=0;L<(1<<m);L++)
{
if(L&a[i-1] || (L&(L<<1)) || (L&(L<<2))) continue; //特判
for(int S=0;S<(1<<m);S++)
{
if(S&a[i] || L&S || (S&(S<<1)) || (S&(S<<2))) continue; //还是特判
for(int FL=0;FL<(1<<m);FL++)
{
if(FL&L || FL&S || FL&a[i-2] || (FL&(FL<<1)) || (FL&(FL<<2))) continue; //仍然是特判
dp[L][S][(i-2)%3+2]=max(dp[L][S][(i-2)%3+2],dp[FL][L][(i-1-2)%3+2]+Sum[S]); //滚动数组的实现方法
}
}
}
for(int L=0;L<(1<<m);L++)
for(int S=0;S<(1<<m);S++)
ans=max(ans,dp[L][S][(n-1)%3+2]); //结束状态可以是最后一行的任何状态
cout<<ans;
return 0;
}
1043: Boooooom
题目描述
Boom!UUZ发飙了。参加ACM的培训本应该有n个人,今天却只来了那么一点点。已知每个学生能来上课的概率为Pi(1≤i≤n),当上课人数少于k时,UUZ会发飙,一旦UUZ发飙,那么以后的课(包括这节课)就都不上了,而如果不发飙,则UUZ还会开下一节课。那么,UUZ能为大家开课的期望大约是多少堂呢?精确到0.1就可以了。
输入
–
有多组输入数据,第一行为一个数字T,代表有T组输入数据 (0<T≤10)。
接下来为T组数据。
每组数据的第一行为两个整数n和k,其中,1≤n≤20,1≤k≤n.
接下来的一行有n个整数1位小数(大于0小于1),表示这n个学生来上课的概率。
输出
对于每组数据,在一行上输出一个实数,表示UUZ上课的期望值。
只要答案的相对误差在10-6以内,或绝对误差在0.1以内,都判为正确。
样例输入
1
3 1
0.1 0.1 0.1
样例输出
0.4
设dp[i][j]表示前i个人中有j个人来上课的概率,初始:dp[0][0]=0;
状态转移方程为:dp[i][j]=dp[i-1][j]*(1-p[i])+dp[i-1][j-1]*p[i];
计算n个人中来上课的人数大于等于k时的概率为 s u m sum sum
则可求的上课天数的期望为:
1
∗
s
u
m
+
2
∗
s
u
m
2
+
…
+
x
∗
s
u
m
x
1*sum+2*sum^2+…+x*sum^x
1∗sum+2∗sum2+…+x∗sumx,
等比数列求和得:
s
u
m
/
(
1
−
s
u
m
)
sum/(1-sum)
sum/(1−sum)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,k;
double p[25];
long double dp[25][25],sum;//dp[i][j]表示前i个人中有j个人来上课的概率
int main() {
int T;
scanf("%d",&T);
while(T-->0) {
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i) {
scanf("%lf",p+i);
}
dp[0][0]=1;
for(int i=1;i<=n;++i) {
for(int j=0;j<i;++j) {
dp[i][j]=dp[i-1][j]*(1-p[i])+dp[i-1][j-1]*p[i];
}
dp[i][i]=dp[i-1][i-1]*p[i];
}
sum=0;//sum表示n个人中来上课的人数大于等于k时的概率
for(int i=k;i<=n;++i) {
sum+=dp[n][i];
}
printf("%.2lf\n",double(sum/(1-sum)));
}
return 0;
}