kdl 现状:P2460 [SDOI2007] 科比的比赛

· · 题解

好好的一个题怎么全用爆搜肘过去啊,太不尊重牢大了这也。

我要为牢大正名所以我联合蓝色大鲸鱼把你们的爆搜全肘飞了(庆祝)!

容易发现 n 这么小所以有大量点是无用的。

于是我们可以贪心,按照获胜概率作为第一关键字,收益作为第二关键字排序。

容易发现无论怎么调整,每一次比赛我们都不可能选到这一行前 n 个以外的点。

于是我们对于 n 行每行保留 n 个点,于是 m 被降到了 O(n^2) 的量级。

考虑我们需要解决的问题是什么,本质上就是给你一个网格,每个网格有概率和收益,你要在上面放互不攻击的車,最大化其所在格子的概率乘积的基础上最大化收益之和。

一列一列的扫过去,直接对于行维护使用状态,暴力转移每个状态的最大概率以及这个概率下的最大收益。 暴力转移时间复杂度就是 $O(nm2^n)$ 的,因为 $m$ 经过压缩于是可以通过。 本题没有 spj,需要保留 12 位。 ```cpp #include<bits/stdc++.h> using namespace std; int v[100005]; bool use[100005]; struct fish{ int id,v; long double x; }a[100005]; bool cmp(fish x,fish y){ if(x.x==y.x)return x.v>y.v; return x.x>y.x; } long double mp[15][100005]; long double dp[2][(1<<10)+5]; int rt[2][(1<<10)+5]; int main(){ int n,m; cin>>n>>m; for(int i=1;i<=m;i++) cin>>v[i]; for(int op=1;op<=n;op++){ for(int i=1;i<=m;i++){ cin>>a[i].x; a[i].id=i; a[i].v=v[i]; mp[op][i]=a[i].x; } sort(a+1,a+1+m,cmp); for(int i=1;i<=n&&i<=m;i++) use[a[i].id]=1; } int op=0; dp[0][0]=1; for(int i=1;i<=m;i++) if(use[i]){ op^=1; for(int j=0;j<(1<<n);j++) dp[op][j]=dp[op^1][j], rt[op][j]=rt[op^1][j]; for(int j=0;j<(1<<n);j++) for(int k=0;k<n;k++) if((1<<k)&j){ long double flc=dp[op^1][j^(1<<k)]*mp[k+1][i]; if(flc>dp[op][j]) rt[op][j]=rt[op^1][j^(1<<k)]+v[i], dp[op][j]=flc; else if(flc==dp[op][j]&&rt[op^1][j^(1<<k)]+v[i]>rt[op][j]) rt[op][j]=rt[op^1][j^(1<<k)]+v[i]; } } printf("%.12llf\n%d",dp[op][(1<<n)-1],rt[op][(1<<n)-1]); return 0; } // kdl 现状 // 在压 // 用dp // kdl 现状:在压用 dp // 图片来源:xmyz 801 黑板 // kdl 现状:看一天文学名著 ```