loj2977 巧克力 (斯坦纳树+随机化)

考虑颜色比较少的时候,第一问可以直接斯坦纳树

第二问考虑二分,每次把每格的权值给成1000+[a[i]>m],就是在个数最少的基础上尽量选小于等于m的

然而颜色太多不能直接做,但可以把每种颜色映射到5以内,这样的话,做一次的正确率就是作为答案的那5种颜色分别被映射到了1~5的概率,就是$\frac{5!}{5^5}=0.0384$,做233次正确率就有$99.989\%$了

 1 #include<bits/stdc++.h>
 2 #include<tr1/unordered_map>
 3 #define CLR(a,x) memset(a,x,sizeof(a))
 4 #define MP make_pair
 5 #define fi first
 6 #define se second
 7 using namespace std;
 8 typedef long long ll;
 9 typedef unsigned long long ull;
10 typedef long double ld;
11 typedef pair<int,int> pa;
12 const int maxn=250,maxp=50;
13 
14 inline ll rd(){
15     ll x=0;char c=getchar();int neg=1;
16     while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
17     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
18     return x*neg;
19 }
20 
21 int step[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
22 int id[maxn][maxn],pct,pos[maxn][2];
23 int N,M,S,K;
24 int f[maxn][maxp],c[maxn],a[maxn],val[maxn];
25 int hsh[maxn],C;
26 
27 queue<int> q;
28 bool flag[maxn];
29 inline void spfa(int s){
30     for(int i=1;i<=pct;i++) if(c[i]!=-1) flag[i]=1,q.push(i);
31     
32     while(!q.empty()){
33         int p=q.front();q.pop();
34         flag[p]=0;
35         int x=pos[p][0],y=pos[p][1];
36         for(int j=0;j<4;j++){
37             int nx=x+step[j][0],ny=y+step[j][1];
38             if(nx<1||ny<1||nx>N||ny>M||c[id[nx][ny]]==-1) continue;
39             int ni=id[nx][ny];
40             if(f[ni][s]>f[p][s]+val[ni]){
41                 f[ni][s]=f[p][s]+val[ni];
42                 if(!flag[ni]) q.push(ni),flag[ni]=1;
43             }
44         }
45     }
46 }
47 
48 inline int solve(){
49     CLR(f,63);
50     for(int i=1;i<=pct;i++) if(c[i]!=-1) f[i][1<<hsh[c[i]]]=val[i];
51     for(int s=1;s<(1<<K);s++){
52         for(int i=1;i<=pct;i++){
53             if(c[i]==-1) continue;
54             for(int ss=(s-1)&s;ss;ss=(ss-1)&s){
55                 f[i][s]=min(f[i][s],f[i][ss]+f[i][s^ss]-val[i]);
56             }
57         }
58         spfa(s);
59     }
60     int ans=1e9;
61     for(int i=1;i<=pct;i++) ans=min(ans,f[i][(1<<K)-1]);
62     return ans;
63 }
64 
65 int main(){
66     //freopen("","r",stdin);
67     srand(19260817);
68     for(int T=rd();T;T--){
69         pct=0,C=0;
70         N=rd(),M=rd(),K=rd();
71         for(int i=1;i<=N;i++){
72             for(int j=1;j<=M;j++) id[i][j]=++pct,pos[pct][0]=i,pos[pct][1]=j;
73         }
74         for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) c[id[i][j]]=rd(),C=max(C,c[id[i][j]]);
75         for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) a[id[i][j]]=rd();
76         int ans1=1e9,ans2=1e9;
77         for(int t=1;t<=233;t++){
78             for(int i=1;i<=C;i++) hsh[i]=rand()%K;
79             int l=0,r=1e6,a1=1e9,a2=1e9;
80             while(l<=r){
81                 int m=(l+r)>>1;
82                 for(int i=1;i<=pct;i++) val[i]=(c[i]==-1?1e8:1000+(a[i]>m));
83                 int re=solve();
84                 if(re>=1e8) break;
85                 a1=re/1000;
86                 int x=a1-(re-a1*1000);
87                 if(x>=(a1+1)/2) a2=m,r=m-1;
88                 else l=m+1;
89             }
90             if(a1<ans1||(ans1==a1&&a2<=ans2)) ans1=a1,ans2=a2;
91         }
92         
93         printf("%d %d\n",ans1==1e9?-1:ans1,ans1==1e9?-1:ans2);
94     }
95     return 0;
96 }

 

posted @ 2019-03-28 11:00  Ressed  阅读(252)  评论(0编辑  收藏  举报