邦德I

8 篇文章 0 订阅

邦德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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值