前言
想到了是DP,打不出来...
后来评讲说是状压DP,我居然自己码出来了,没有看代码!!!开心!功力++!
题目
每个人都知道詹姆斯邦德,著名的007,但很少有人知道很多任务都不是他亲自完成的,而是由他的堂弟们吉米邦德完成(他有很多堂弟),詹姆斯已经厌倦了把一个个任务分配给一个个吉米,他向你求助。
每个月,詹姆斯都会收到一些任务,根据他以前执行任务的经验,他计算出了每个吉米完成每个任务的成功率,要求每个任务必须分配给不同的人去完成,每个人只能完成一个任务。
请你编写程序找到一个分配方案使得所有任务都成功完成的概率。
Input
输入第一行包含一个整数N,表示吉米邦德的数量以及任务的数量(正好相等,1<=N<=20)。
接下来N行,每行包含N个0到100之间整数,第i行的第j个数Aij表示吉米邦德i完成任务j成功的概率为Aij%
Output
输出所有任务成功完成最大的概率,结果保留6位小数。
Sample Input
输入1: 2 100 100 50 50 输入2: 2 0 50 50 0 输入3: 3 25 60 100 13 0 50 12 70 90
Sample Output
输出1: 50.000000 输出2: 25.000000 输出3: 9.100000
分析
【状压DP】板题
dp[ i ]表示:i的二进制数有k个1,则为前k个人做k件事的最大效率
(自己觉得好多题解都很抽象,这里写细一点)举个栗子——
dp[5],5的二进制数为101,有两个1分别在位置1和3上,表示前2个人做了任务1和3
接下来安排第2+1=3个人做任务,就枚举101中是0的一位,其位置是2,那么他就做任务2
转移例子:dp[ 5+2^1 ]=max( dp[ 5+2^1 ] , dp[ 5 ]*a[ 3 ][ 2 ]/100 )(a给的是百分数)
最后设x=111....1(n个1),答案为dp[ x ],再化为百分数:ans=dp[ x ]*100
考试暴力50分代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=20;
int a[MAXN+5][MAXN+5],f[MAXN+5];
bool vis[MAXN+5];
int n;
double ans=0;
void dfs(int id,int num)
{
f[id]=num;
vis[num]=1;
if(id==n)
{
double tmp=1.0;
for(int i=1;i<=n;i++)
tmp*=(double)a[i][f[i]]/100;
ans=max(ans,tmp);
}
for(int i=1;i<=n;i++)
if(!vis[i])
dfs(id+1,i);
vis[num]=0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
dfs(1,i);
printf("%.6lf",ans*100);
return 0;
}
/*
我去,这就是分组背包吧...
但是前面的选择会影响后面的选择...(后效性?)
先暴力... 希望能有一些的分...
*/
AC代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=20;
int a[MAXN+5][MAXN+5],pre[MAXN+5],b[MAXN+5];
double f[1050000];//前i个人做i件事的最大概率
int n,tot=1;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
//预处理,方便将二进制转十进制,为状压铺路:
pre[1]=1;
for(int i=2;i<=n+1;i++)
{
pre[i]=pre[i-1]*2;
if(i<=n)
tot+=pre[i];
}
for(int S=0;S<=(1<<n)-1;S++)//枚举集合
{
int cnt=0,tmp=0;
for(int i=1;i<=n;i++)
if(1<<(i-1)&S)//第i位是1
cnt++,tmp+=pre[i];
for(int i=1;i<=n;i++)
if(!(1<<(i-1)&S))//枚举一个是0的位
{
if(!f[tmp])//f[tmp]还没有值,不能直接乘
f[tmp+pre[i]]=1.0*a[cnt+1][i]/100;
else
f[tmp+pre[i]]=max(f[tmp+pre[i]],f[tmp]*1.0*a[cnt+1][i]/100);
}
}
printf("%.6f",f[tot]*100);
return 0;
}