您的位置:首页 > 其它

[折半搜索 剪枝 随机化染色] 2015 计蒜之道 复赛 腾讯的星钻增值服务

2016-11-12 11:12 351 查看
题解里说的很详细 实在没有想到0.94的出错率 都可以随机化 不过在数据范围小 可以进行较多次随机的情况下 每次都出错的几率就很小了

http://blog.jisuanke.com/?p=146

先让我们来思考一下,如果这个问题被简化成“七种不同的星数分别为1, 2, 3, … 7”,这个问题应该如何解?

由于背包的最大负重和代价都比较大,所以直接做 0/1 背包难度稍大。但是由于总的星钻个数比较少,因此我们可以使用折半的方法,分别枚举四种、其余三种星钻怎么选取,得到所有可能的方案,再考虑合并(这里需要排序)。

一种简单的考虑是,假设每个星钻的星数是在 1~7 种 uniformly random 的,那么每种星数的星钻大致会在15(≈ 100/7)个左右,所以枚举的量会在154 ≈ 5 × 104。

更一般的来看,考虑每种星钻的个数已知的情况下,我们可以通过枚举如何按照星数的不同分 2 组(共27 − 2种不同的分组方案),使得分出的 2 组内部组合数的最大值最小。所以,无论 7 种星钻的个数怎么分布,最坏情况下我们需要枚举的量在1.3 × 105以内。证明如下:

假设在最优分组的情况下,较小的组合数为
prod???,较大的组合数为 prodmax。由此,prod??? ×N ≥
prodmax。 又因为 prod??? × prodmax 为所有数的总乘积,这些数的总和为 ?,所以总乘积最大为 (N/7)7。所以




基于 random simulation 最坏为 154,应该可以找到理论证,但这里有这个很松的界就足够了。

接下来,我们来看看星数很多的情况。核心的思想是随机染色。

首先将不同的星数随机映射到 1 到 7。假设正确答案只对应一种方案,那么在一次随机染色种,这个最佳方案中的 7 种星数被映射到 7 种不同的颜色的概率为




在随机染色之后,就可以用上文提到的简单版本的方法来解决。

假设我们反复执行随机染色 1000 次,那么每一次都错误的概率为(1 − 0.006)1000 ≈ 0.002,已经是一个可以接受的范围了。如果没有通过,可以修改随机的种子后再次提交。

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;

inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}

inline void read(int &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=105;

int n,m;
int S
,W
,C
,Clr
;
int cot[7],lst[7]
;
int clr[7];

struct abcd{
ll p,t;
abcd(ll p=0,ll t=0):p(p),t(t) { }
bool operator < (const abcd &B) const{
return p<B.p;
}
}rec[150005];
int tot=0;

inline void dfs1(int cur,ll p,ll t){
if (p>m) return;
if (cur==7){
rec[++tot]=abcd(p,t);
return;
}
if (clr[cur]){
for (int i=1;i<=cot[cur];i++)
dfs1(cur+1,p+W[lst[cur][i]],t+C[lst[cur][i]]);
}else
dfs1(cur+1,p,t);
}

ll ret,Ans;

inline void dfs2(int cur,ll p,ll t){
if (p>m) return;
if (t>Ans) return;
if (cur==7){
int iter=upper_bound(rec+1,rec+tot+1,abcd(m-p,1LL<<60))-rec-1;
if (iter)
ret=min(ret,t+rec[iter].t);
return;
}
if (!clr[cur]){
for (int i=1;i<=cot[cur];i++)
dfs2(cur+1,p+W[lst[cur][i]],t+C[lst[cur][i]]);
}else
dfs2(cur+1,p,t);
}

inline ll Solve(){
for (int i=0;i<7;i++) cot[i]=0;
for (int i=1;i<=n;i++) lst[S[i]][++cot[S[i]]]=i;
for (int j=0;j<7;j++) if (!cot[j]) return 1LL<<60;
int mint0,mint1=1<<30,k;
for (int i=0;i<(1<<7);i++){
int t[2]={1,1};
for (int j=0;j<7;j++)
t[i>>j&1]*=cot[j];
if (max(t[0],t[1])<max(mint0,mint1))
mint0=t[0],mint1=t[1],k=i;
}
for (int j=0;j<7;j++) clr[j]=k>>j&1;
tot=0; dfs1(0,0,0);
sort(rec+1,rec+tot+1);
int pnt=0;
for (int i=1;i<=tot;i++)
if (!pnt || rec[i].t<=rec[pnt].t)
rec[++pnt]=rec[i];
tot=pnt;
ret=1LL<<60;
dfs2(0,0,0);
return ret;
}

int sx
,icnt;
int mapping
;

inline int Bin(int x){
return lower_bound(sx+1,sx+icnt+1,x)-sx;
}

int main(){
const int Test=1000;
int Q; srand(10086);
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(Q);
while (Q--){
read(n); read(m);
icnt=0;
for (int i=1;i<=n;i++) read(S[i]),read(W[i]),read(C[i]),Clr[i]=S[i],sx[++icnt]=S[i];
sort(sx+1,sx+icnt+1); icnt=unique(sx+1,sx+icnt+1)-sx-1;
Ans=1LL<<60;
for (int t=1;t<=Test;t++){
for (int i=1;i<=icnt;i++) mapping[i]=rand()%7;
for (int i=1;i<=n;i++) S[i]=mapping[Bin(Clr[i])];
//for (int i=1;i<=n;i++) S[i]=rand()%7;
Ans=min(Ans,Solve());
}
Ans==1LL<<60?printf("-1\n"):printf("%lld\n",Ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: