码迷,mamicode.com
首页 > 其他好文 > 详细

模拟14 题解

时间:2019-08-08 21:25:50      阅读:75      评论:0      收藏:0      [点我收藏+]

标签:nbsp   ems   位置   display   check   说明   ret   etc   div   

T2

技术图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<vector>
 8 #define R register
 9 #define INF 1061109567
10 using namespace std;
11 const int maxn=250005;
12 int n,m,a[505][505];
13 int d[maxn],v[maxn];
14 vector<int>r[505],c[505];
15 int st,en;
16 struct node{
17     int u,w,v,nxt;
18 }e[16*maxn];int h[maxn],nu;
19 void add(int x,int y,int z)
20 {
21     e[++nu].u=x;
22     e[nu].v=y;
23     e[nu].nxt=h[x];
24     e[nu].w=z;
25     h[x]=nu;
26 }
27 int cal(int x,int y){return (x-1)*m+y;}
28 void spfa()
29 {
30     queue<int>q;
31     memset(d,0x3f,sizeof d);
32     d[st]=0,v[st]=1;
33     q.push(st);
34     while(q.size())
35     {
36         int x=q.front();
37         q.pop(),v[x]=0;
38         for(int i=h[x];i;i=e[i].nxt)
39         {
40             int y=e[i].v;
41             if(d[y]>d[x]+e[i].w){
42                 d[y]=d[x]+e[i].w;
43                 if(!v[y])q.push(y),v[y]=1;
44             }
45         }
46     }
47 }
48 int main()
49 {
50     scanf("%d%d",&n,&m);
51     for(int i=1;i<=n;i++)
52     {
53         char ci[505];
54         scanf("%s",ci+1);
55         for(int j=1;j<=m;j++)
56         {
57             if(ci[j]==#)a[i][j]=1,r[i].push_back(j),c[j].push_back(i);
58             if(ci[j]==C)st=cal(i,j);
59             if(ci[j]==F)en=cal(i,j);
60         }
61     }
62     for(int i=1;i<=n;i++)
63         for(int j=1;j<=m;j++)
64         {
65             if(a[i][j])continue;
66             int x=cal(i,j);
67             if(!a[i][j-1])add(x,cal(i,j-1),1);
68             if(!a[i][j+1])add(x,cal(i,j+1),1);
69             if(!a[i-1][j])add(x,cal(i-1,j),1);
70             if(!a[i+1][j])add(x,cal(i+1,j),1);
71             int t1=lower_bound(c[j].begin(),c[j].end(),i)-c[j].begin();
72             int t2=lower_bound(r[i].begin(),r[i].end(),j)-r[i].begin();
73             int d1=min(c[j][t1]-i,i-c[j][t1-1]),d2=min(r[i][t2]-j,j-r[i][t2-1]);
74             int md=min(d1,d2);
75             if(c[j][t1]-1!=i)   add(x,cal(c[j][t1]-1,j),md);
76             if(c[j][t1-1]+1!=i) add(x,cal(c[j][t1-1]+1,j),md);
77             if(r[i][t2]-1!=j)   add(x,cal(i,r[i][t2]-1),md);
78             if(r[i][t2-1]+1!=j) add(x,cal(i,r[i][t2-1]+1),md);
79         }
80     spfa();
81     int ans=d[en];
82     if(ans>=INF)puts("no");
83     else printf("%d\n",ans);
84 }
View Code

 T3

引问:1-N的任意序列,每个数加上或减去一个值,使的所有的数成为一个相同的数,求变化值之和的最小值

引理:变化后的数为这个序列中位数,此时变化值之和最小

证明:先将这个序列排序,设变化后的相同值为p,则小于p的所有数变化值之和为$1+2+...+(p-1)$大于p的数变化值之和为$1+2+...+n-p+1$

等差求和并化简为$p^2-(n+1)p+(n+n^2)/2$,单谷函数,由图像,取$(n+1)/2$时取到最小值

推广:任意一个序列,进行以上操作,都要变为这个序列的中位数,才能使解最优

 

30%算法 n^3暴力

最外层枚举屋顶的位置,然后枚举屋顶高度,再扫一边统计答案

60%算法

最外层枚举屋顶的位置,思考如何将枚举屋顶高度的过程优化,

定义 s[i]=a[i]+abs(pos-i)  ,H为最高高度,det[i]为其变化的高度;

a[i]+abs(pos-i)=H-det[i],左式形成了一个序列,将左式序列变化det值使得它成为一个定值H

使用以上证明,所以就是找这个序列的中位数$O(n^2)$

技术图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #define R register
 7 #define ll long long
 8 using namespace std;
 9 const int maxn=100005;
10 inline int read()
11 {
12     int f=1,x=0;char ch=getchar();
13     while(ch>9||ch<0){if(ch==-)f=-1;ch=getchar();}
14     while(ch<=9&&ch>=0){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
15     return f*x;
16 }
17 inline int ab(int x){return (x<0)?(-x):x;} 
18 int n,a[maxn],s[maxn];
19 int main()
20 {
21 //    freopen("data","r",stdin);
22     n=read();
23     int mx=n;
24     for(R int i=1;i<=n;++i)
25         a[i]=read(),mx=max(a[i],mx);
26     R ll ans=0x3f3f3f3f3f3f3f3f;
27     for(R int pos=1;pos<=n;++pos)
28     {
29         for(R int i=1;i<=n;++i)    s[i]=a[i]+ab(pos-i);
30         R int mid=(n+1)>>1;
31         nth_element(s+1,s+mid,s+n+1);
32         R int val=s[mid],mx=max(pos,n-pos+1);
33         val=max(val,mx);
34         ll tot=0;
35         for(R int i=1;i<=n;i++)
36             tot+=ab(val-s[i]);
37         ans=min(ans,tot);
38     }
39     printf("%lld\n",ans);
40 }
View Code

理论60%算法2

发现一个性质:变化的总值sum  关于 屋顶高度h 的图像为单谷函数

可以这么想:如果屋顶高度特别高,那么每一个i都是增大的,sum就会随h单调递增,反之递减当h适中时,代价会最小

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 long long n,h[100010],ans=0x7fffffffffffffff,now,k,maxx;
 4 long long check(const int he,const int pos){
 5     long long an=0;
 6     for(int i=1;i<=n;i++)
 7         an+=abs(he-abs(pos-i)-h[i]);
 8     return an;
 9 }
10 int main(){
11     scanf("%lld",&n);
12     for(int i=1;i<=n;i++) scanf("%lld",&h[i]),maxx=max(maxx,h[i]);
13     for(int i=1;i<=n;i++){
14         long long l=n,r=maxx*3;
15         while(l<r-2){
16             long long lmid=l+(r-l)/3,rmid=l+(r-l)*2/3;
17             if(check(lmid,i)<=check(rmid,i)) r=rmid;
18             else l=lmid;
19         }
20         //cout<<i<<" "<<l<<endl;
21         ans=min(ans,check(l,i));
22         ans=min(ans,check(l+1,i));
23         ans=min(ans,check(l+2,i));
24     }
25     printf("%lld",ans);
26 }
mikufun

100%算法:「乱搞」「模拟退火」

打表发现,sum关于屋顶位置是个多谷函数(就是没有任何规律)

模拟退火可A

技术图片
 1 #include<bits/stdc++.h>
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<cmath>
 7 #include<algorithm>
 8 #define R register
 9 #define ll long long
10 using namespace std;
11 const int maxn=100005;
12 const double eps=1e-5;
13 const double delta=0.983;
14 const double jl=1;
15 inline int read()
16 {
17     int f=1,x=0;char ch=getchar();
18     while(ch>9||ch<0){if(ch==-)f=-1;ch=getchar();}
19     while(ch<=9&&ch>=0){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
20     return f*x;
21 }
22 inline int ab(int x){return (x<0)?(-x):x;}
23 int n,a[maxn],s[maxn];
24 ll ans=0x3f3f3f3f3f3f3f3f;
25 inline ll cal(int pos)
26 {
27     for(int i=1;i<=n;i++)    s[i]=a[i]+ab(pos-i);
28     int mid=(n+1)>>1;
29     nth_element(s+1,s+mid,s+n+1);
30     int val=s[mid],mx=max(pos,n-pos+1);
31     val=max(val,mx);
32     ll tot=0;
33     for(int i=1;i<=n;i++)
34         tot+=ab(val-s[i]);
35     return tot;
36 }
37 void SA()
38 {
39     double T=1000;
40     int now=(n+1)>>1;
41     ll nowans=cal(now);
42     while(T>eps)
43     {
44         int tmp=now+(2LL*rand()-RAND_MAX)*T*0.000001;
45         if(T<jl) tmp=max(tmp,1),tmp=min(tmp,n);
46         else tmp=(tmp%n+n)%n+1;
47         ll tmpans=cal(tmp);
48         ll DE=tmpans-nowans;
49         if(DE<0||exp(-DE/T)*RAND_MAX>rand())nowans=tmpans,now=tmp;
50         if(tmpans<ans)ans=tmpans;
51         T*=delta;
52     }
53 }
54 int main()
55 {
56     //freopen("data","r",stdin);
57     srand(time(0));
58     n=read();
59     for(R int i=1;i<=n;++i)
60         a[i]=read();
61     SA();
62     printf("%lld\n",ans);
63     return 0;
64 }
View Code

推了一下午的式子,然后发现,最一开始的定义就有些问题,所以努力无果QAQ

自己瞎颓式子的时候一定要严谨证明

有一些东西不必要用个式子表示,用理说明(感性理解)就好

打表打表找规律

模拟14 题解

标签:nbsp   ems   位置   display   check   说明   ret   etc   div   

原文地址:https://www.cnblogs.com/casun547/p/11319133.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!