题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3502
题目大意:有N道题,分别为A,B,C,D...,现在每道题答对的概率不相互独立,给你一个N*N的矩阵,其中map[i][j]表示回答过i题后(无论是否答对)再回答j题,答对j题的概率,现在求一种答题顺序,使得 答对的题目数的期望最大。并输出字典序最小的最优答题方案。
解题思路:求一个答题顺序 比较简单,关键怎末保存最小的答题顺序,题中用一个数组保存结果,同时注意精度问题
状态转移方程为:dp[i]=dp[i|(1<<j)]+p(最大作对概率)
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
#define eps 1e-8
double map[11][11],dp[1<<12];
string s[1<<12]; //s为每个状态的做题顺序
int n,t;
void solve()
{
int num=(1<<n);
for(int i=1;i<num;i++)
{
dp[i]=-1;
for(int j=0;j<n;j++)
{
if(i&(1<<j))
{
double p=0;
for(int k=0;k<n;k++) //查找解决j题的最大概率
if(i&(1<<k))
p=max(p,map[k][j]);
p=dp[i^(1<<j)]+p;
//此处的精度控制 dp[i]-eps<=p<=dp[i]+eps
//故 如果p>dp[i]+eps 说明 p 一定大于dp[i],
if(p>dp[i]+eps||(p>dp[i]-eps &&s[i]>s[i^(1<<j)]))
{
dp[i]=p;
s[i]=s[i^(1<<j)];//如果s[i]>s[i^(1<<j)]当前字典序不是最优的,所以更新
s[i]+=('A'+j);
}
}
}
}
printf("%.2lf\n%s\n",dp[num-1],s[num-1].c_str());
//string 类中用printf输出s串,当然也可以用 cout
}
int main()
{
scanf("%d",&t);
while(t--)
{
int a;
scanf("%d",&n);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&a),map[i][j]=a*1.0/100.0;
solve();
}
return 0;
}