C - Crusher’s Code( 概率dp )
题意(翻译润色了一下):
^▽^神秘的三角洲一直流传着一个关于排序的问题。
你在旅途中遇到了Monly和Carlos,他们都对自己解决这个问题的方案很有信心。
Monty的代码是这样的:
while (!sorted(a))
{
int i = random(n) ;
int j = random(n) ;
if (a[min(i,j)] > a[max(i,j)])
swap(a[i], a[j]) ;
}
这是Carlos的代码:
while (!sorted(a))
{
int i = random(n-1) ;
int j = i + 1 ;
if (a[i] > a[j])
swap(a[i], a[j]) ;
}
请聪明的你帮争论不休的他们确定哪种算法更好。
数组最长为8,计算两种算法在给定的序列情况下,完成排序需要迭代次数的期望。
思路:
容易想到是概率dp,所有的状态只有8!=40320种,hash一下数组可以进行维护,考虑状态迁移,每个状态都往更有序的状态才能进行迁移,比如[2 3 1] 往 [1 3 2] 和 [ 2 1 3 ]迁移,往其他地方迁移就等于本身,那么所有状态迁移到最后都是[1 2 3] 无法再进行迁移了,那么我们就让dp[1 2 3] = 0, 往上更新,反推dp的定义就是当前状态到123期望需要几步。
对于第一个算法
now表示当前状态的hash值, nxt表示往下迁移一次的hash值。
那么当前的状态now,必定由它换位置可以转移到的所有情况next状态的期望各自*概率( 概率是1/n方根据题意得到 ),再加上它自己原封不动的期望*不动的概率(因为有可能不交换),最后+1表示过了一回合。
C表示状态迁移的种类个数。
因为涉及到自己到自己进行更新,所以进行移项,很常用的技巧。
移项化简后得到
对于第二个算法
移项化简后得到
代码:
#include <bits/stdc++.h>
using namespace std;
int a[12],b[12],n,bot;
map<int,double> mp;
int Hash( int *c )
{
int now = 0;
for ( int i=0; i<n; i++ ) now=now*8+c[i];
return now;
}
double dfs( int *c )
{
int has = Hash(c);
if ( has==bot ) return 0;
if ( mp[has]>0 ) return mp[has];
double sum = 0;
int cnt = 0;
for ( int i=0; i<n; i++ ) {
for ( int j=0; j<n; j++ ) {
int ii = min(i,j);
int jj = max(i,j);
if ( c[ii]>c[jj] ) {
cnt ++;
swap(c[ii],c[jj]);
sum += dfs(c);
swap(c[ii],c[jj]);
}
}
}
sum += n*n;
sum /= cnt;
return mp[has] = sum;
}
double dfs2( int *c )
{
int has = Hash(c);
if ( has==bot ) return 0;
if ( mp[has]>0 ) return mp[has];
double sum = 0;
int cnt = 0;
for ( int i=0; i<n-1; i++ ) {
int ii = i;
int jj = i+1;
if ( c[ii]>c[jj] ) {
cnt ++;
swap(c[ii],c[jj]);
sum += dfs2(c);
swap(c[ii],c[jj]);
}
}
sum += (n-1);
sum /= cnt;
return mp[has] = sum;
}
int main()
{
int T;cin>>T;
while ( T-- ) {
cin>>n; mp.clear();
for ( int i=0; i<n; i++ ) cin>>a[i];
for ( int i=0; i<n; i++ ) b[i] = a[i]; /// 离散化,方便hash
sort(b,b+n);
for ( int i=0; i<n; i++ ) a[i]=lower_bound(b,b+n,a[i])-b;
for ( int i=0; i<n; i++ ) b[i]=a[i];
sort(b,b+n);
bot = 0;
for ( int i=0; i<n; i++ ) bot=bot*8+b[i];
double ans = dfs(a);mp.clear();
double ans2 = dfs2(a);
printf("Monty %.6f Carlos %.6f\n",ans,ans2);
}
return 0;
}