邦德I
Time Limits: 2000 ms Memory Limits: 65536 KB
Description
每个人都知道詹姆斯邦德,著名的007,但很少有人知道很多任务都不是他亲自完成的,而是由他的堂弟们吉米邦德完成(他有很多堂弟),詹姆斯已经厌倦了把一个个任务分配给一个个吉米,他向你求助。
每个月,詹姆斯都会收到一些任务,根据他以前执行任务的经验,他计算出了每个吉米完成每个任务的成功率,要求每个任务必须分配给不同的人去完成,每个人只能完成一个任务。
请你编写程序找到一个分配方案使得所有任务都成功完成的概率。
Input
输入第一行包含一个整数N,表示吉米邦德的数量以及任务的数量(正好相等,1<=N<=20)。
接下来N行,每行包含N个0到100之间整数,第i行的第j个数Aij表示吉米邦德i完成任务j成功的概率为Aij%
Output
输出所有任务成功完成最大的概率,结果保留6位小数。
Sample Input
Input1:
2
100 100
50 50
Input2:
2
0 50
50 0
Input3:
3
25 60 100
13 0 50
12 70 90
Sample Output
Output1:
50.000000
Output2:
25.000000
Output3:
9.100000
解题思路
我们一看数据范围!呀!(n<=20),于是我就立刻想到了状态压缩DP
设f[j]表示为已经完成的任务用二进制状态压缩表示为为j的最大成功率。
令k表示为j的二进制中1的个数,那么显然f[j]是由这前k个邦德完成的。
有了这种思路,我们可以将上一种状态的任意一个0掰为1,更新后一个状态,这里可以边BFS,边更新。
Codes:
#include<cstring>
#include<cstdio>
#define fo(i,x,y) for(int i=x;i<=y;i++)
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
int n,h,t,data[1048577][2];
bool b[1048576];
double a[21][21],f[1048576];
int Log(int);
int main()
{
scanf("%d",&n);
fo(i,1,n)fo(j,1,n)scanf("%lf",&a[i][j]);
memset(b,1,sizeof(b));
memset(f,0,sizeof(f));
h=0;t=1;b[0]=0;data[1][0]=0;data[1][1]=0;f[0]=100;
while(h<t)
{
int now=data[++h][0],s=data[h][1];
fo(o,0,n-1)if((now & (1<<o))==0)
{
int x=now+(1<<o);
f[x]=max(f[x],f[now]*a[s+1][o+1]/100);
if(b[x])
{
b[x]=0;
data[++t][0]=x;data[t][1]=s+1;
}
}
}
printf("%.6lf",f[(1<<n)-1]);
}
int Log(int num)
{
int tot=0;
while(num>0){tot++;num>>=1;}
return tot;
}
也可以首先预处理出有i个1构成的二进制数的十进制形式,这样看起来比较直接,但是会慢一点。
Codes:
#include<cstdio>
#include<cstring>
#define fo(i,x,y) for(int i=x;i<=y;i++)
using namespace std;
double ans,f[2000000],a[21][21];
int n,kind[21][200000];
double max(double x,double y){return x>y?x:y;}
void init(int,int,int);
int main()
{
scanf("%d",&n);
memset(kind,0,sizeof(kind));
init(1,0,0);
fo(i,1,n)fo(j,1,n)scanf("%lf",&a[i][j]);
memset(f,0,sizeof(f));
fo(i,1,n)f[1<<(i-1)]=a[1][i];
fo(i,2,n)
{
fo(j,1,kind[i][0])fo(k,1,n)if(((1<<(k-1))& kind[i][j])>0)
{
f[kind[i][j]]=max(f[kind[i][j]],f[kind[i][j]-(1<<(k-1))]*a[i][k]/100.0);
}
}
printf("%.6lf",f[(1<<n)-1]);
}
void init(int w,int num,int tot)
{
if(w>n)
{
kind[tot][++kind[tot][0]]=num;
return;
}
init(w+1,num,tot);
init(w+1,num+(1<<(w-1)),tot+1);
}