NOIP模拟测试14

考完19了再写14,我也是够咕的。

14的题很好,也充分暴露了我的问题。

T1是个分析性质推结论的题

对于区间[L,R],不妨设a[L]!=a[R],那么两个端点对答案没有贡献,也就是[L+1,R],[L,R-1]都是符合题意的区间。

即 最优的区间的两个端点一定相等

然后把每个数的位置和大小作为区间的两端点,统计答案即可。

 1 #include<bits/stdc++.h>
 2 #define F(n) for(int i=1;i<=n;i++)
 3 int a[500005],sum[500005],n,ans;
 4 std::vector< std::pair<int,int> >k[500005<<1];
 5 int main(){
 6     scanf("%d",&n);
 7     F(n)scanf("%d",&a[i]),sum[i]=sum[i-1]+(a[i]==i);ans=sum[n];
 8     F(n)k[a[i]+i].push_back(std::make_pair(std::min(i,a[i]),std::max(i,a[i])));
 9     F((n<<1))sort(k[i].begin(),k[i].end());
10     F((n<<1))
11         for(int t=k[i].size(),j=t-1,last=0;j>=0;j--,last++)
12             ans=std::max(ans,last+1+sum[k[i][j].first-1]+sum[n]-sum[k[i][j].second]);
13     std::cout<<ans;
14 }
View Code

T2走格子

关键在于如何将题目抽象成图论模型,而不是只一味的搜索。

这道题没有太多的限制,只有移动到每个格子的距离的不同。

因此直接建边,跑dijstra即可

 1 #include<bits/stdc++.h>
 2 #define MAXN 505
 3 using namespace std;
 4 const int h[5]={0,1,-1,0,0},l[5]={0,0,0,1,-1};
 5 int mp[MAXN][MAXN],endx,endy,n,m,tot=0;
 6 priority_queue<pair<int,int> >Q;
 7 vector<int>zh[MAXN],zl[MAXN];
 8 bool vst[MAXN*MAXN];
 9 int ans=0x7f7f7f7f,stax,stay;
10 int p[MAXN][MAXN],d[MAXN*MAXN],nxt[MAXN*MAXN*100],val[MAXN*MAXN*100],to[MAXN*MAXN*100],cnt,head[MAXN*MAXN];
11 void add(int u,int v,int w)
12 {
13     to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;val[cnt]=w;
14     return ;
15 }
16 int dijstra()
17 {
18     memset(d,0x3f,sizeof(d));d[p[stax][stay]]=0;
19     Q.push(make_pair(0,p[stax][stay]));
20     while(Q.size())
21     {
22         int k=Q.top().second;Q.pop();
23         if(k==p[endx][endy])return d[k];
24         if(vst[k])continue;
25         vst[k]=1;
26         for(int i=head[k];i;i=nxt[i])
27         {
28             int y=to[i];
29             if(d[y]>d[k]+val[i])
30             {
31                 d[y]=d[k]+val[i];
32                 Q.push(make_pair(-d[y],y));
33             }
34         }
35     }
36     return 0x7f7f7f7f;
37 }
38 int main()
39 {
40     scanf("%d%d",&n,&m);
41     for(int i=1;i<=n;i++)
42     {
43         for(int j=1;j<=m;j++)
44         {
45             p[i][j]=++tot;
46             char c=getchar();
47             while(c!='C'&&c!='F'&&c!='.'&&c!='#')c=getchar();
48             if(c=='C')stax=i,stay=j,mp[i][j]=1;
49             else if(c=='F')endx=i,endy=j,mp[i][j]=1;
50             else if(c=='.')mp[i][j]=1;
51             else mp[i][j]=0;
52         }
53     }
54     for(int i=1;i<=n;i++)
55         for(int j=1;j<=m;j++)
56             if(!mp[i][j])zh[i].push_back(j);
57     for(int j=1;j<=m;j++)
58         for(int i=1;i<=n;i++)
59             if(!mp[i][j])zl[j].push_back(i);
60     for(int i=1;i<=n;i++)
61         for(int j=1;j<=m;j++)
62         {
63             if(!mp[i][j])continue;
64             if(i^1&&mp[i-1][j])add(p[i][j],p[i-1][j],1);
65             if(i^n&&mp[i+1][j])add(p[i][j],p[i+1][j],1);
66             if(j^1&&mp[i][j-1])add(p[i][j],p[i][j-1],1);
67             if(j^m&&mp[i][j+1])add(p[i][j],p[i][j+1],1);
68             int up=*(--lower_bound(zl[j].begin(),zl[j].end(),i));
69             int down=*(lower_bound(zl[j].begin(),zl[j].end(),i));
70             int lef=*(--lower_bound(zh[i].begin(),zh[i].end(),j));
71             int rig=*(lower_bound(zh[i].begin(),zh[i].end(),j));
72             int dis=min(i-up,min(down-i,min(j-lef,rig-j)));
73             add(p[i][j],p[up+1][j],dis);
74             add(p[i][j],p[down-1][j],dis);
75             add(p[i][j],p[i][lef+1],dis);
76             add(p[i][j],p[i][rig-1],dis);
77         }
78     ans=dijstra();
79     if(ans==0x7f7f7f7f)cout<<"no"<<endl;
80     else cout<<ans<<endl;
81     return 0;
82 }
View Code

T3柱状图  好题标记

这道题考察的是三分。

观察一下可知对于某一位置来说,其高度大小与最终答案的关系是单谷的。

(当你得到一个最优答案的时候,如果变大会使其余的柱子在原基础上再上升一个,反之亦然)

于是60分做法诞生:

枚举位置,三分高度,统计答案   O(n^2logn)

 1 #include<bits/stdc++.h>
 2 #define MAXN 100005
 3 #define int long long
 4 #define F(x) for(int i=1;i<=n;i++)
 5 #define re register
 6 #define min(a,b) (a)<(b)?(a):(b)
 7 using namespace std;
 8 int a[MAXN],now,n,maxn;
 9 inline int Get(int h)
10 {
11     int ans=abs(h-a[now]);
12     F(n)
13     {
14         if(i==now)continue;
15         if(h-abs(i-now)<=0)return 0x7f7f7f7f7f7f7ff;
16         ans+=abs(a[i]-(h-abs(i-now)));
17     }
18     return ans;
19 }
20 inline int San_fen()
21 {
22     int l=now,r=maxn*2;
23     while(l+2<r)
24     {
25         int lmid=l+(r-l)/3;
26         int rmid=r-(r-l)/3;
27         if(Get(lmid)<Get(rmid))r=rmid;
28         else l=lmid;
29     }
30     return min(Get(l),min(Get(l+1),Get(l+2)));
31 }
32 signed main()
33 {
34     re int ans=0x7f7f7f7f7f7f7ff;
35     scanf("%lld",&n);
36     F(n)scanf("%lld",&a[i]),maxn=max(maxn,a[i]);
37     for(now=1;now<=n;now++)
38         ans=min(ans,San_fen());
39     cout<<ans<<endl;
40     return 0;
41 }
View Code

考虑优化,对于统计答案,我们分情况讨论一下

(讨论个毛线)详情见这里

主要说一下skyh的垃圾退火(雾)

模拟退火是一个特别神(kan)奇(lian)的算法。

是这样的:

根据物理学常识我们可以知道,物体内能越小越稳定。

所谓的模拟退火,就是模拟这个过程。

1.设置初始温度T0

2.找出下一个目标位置

3.尝试用新的答案更新旧的答案

4.降温

是不是一脸蒙蔽?

详细来说:

 1 while(T>eps)                                                          //终止边界
 2 {
 3     tmp=now+(rand()*2ll-RAND_MAX)*T*0.000001;                         //温度越高,变化越大
 4     if(T<1.0)tmp=max(tmp,1ll),tmp=min(tmp,n);                         //当温度小于一定程度的时候,让下一个位置尽量小地变化
 5     else tmp=(tmp%n+n)%n+1;                                           //让下一个位置在要求的范围内
 6     tmpans=calc(tmp);D=-fabs(tmpans-nowans);                          //找到新的解
 7     if(tmpans<nowans||exp(D/T)*RAND_MAX>rand())nowans=tmpans,now=tmp; //根据概率确定答案是否改变
 8     if(tmpans<ans)ans=tmpans;                                         //更新最终的答案
 9     T*=0.975;                                                         //降温
10 }

根据前人的经验,这个更新的概率是exp(D/T)(D是变化量绝对值的相反数,T是当前温度)

最后附上完整代码

 1 #include<bits/stdc++.h>
 2 #define MAXN 100005
 3 #define int long long 
 4 using namespace std;
 5 int yx[MAXN],n,now;
 6 struct Bit{
 7     int tr[2][MAXN];
 8     void insert(int x,int val,int opt)
 9     {
10         for(int i=x;i<=n;i+=i&-i)tr[0][i]+=val,tr[1][i]+=opt;
11         return ;
12     }
13     pair<int,int> query(int x)
14     {
15         int num=0,ans=0;
16         for(int i=x;i;i-=i&-i)num+=tr[1][i],ans+=tr[0][i];
17         return make_pair(ans,num);
18     }
19 }T[2];
20 int sa[MAXN],sb[MAXN],ap[MAXN],bp[MAXN],ans=0x7f7f7f7f7f7f7ff,maxa,a[MAXN],b[MAXN];
21 inline int check(int x)
22 {
23     if(x<now||x<(n-now+1))return 0x7f7f7f7f7f7f7ff;
24     int ans=abs(x-yx[now]);
25     int num=upper_bound(a+1,a+n+1,x-now)-a-1;
26     pair<int,int> w=T[0].query(num);
27     ans+=abs(w.first-w.second*(x-now))+abs(sa[now-1]-w.first-(now-1-w.second)*(x-now));
28     num=upper_bound(b+1,b+n+1,x+now)-b-1;
29     w=T[1].query(num);
30     ans+=abs(w.first-(now+x)*w.second)+abs(sb[n]-sb[now]-w.first-(n-now-w.second)*(now+x));
31     return ans;
32 }
33 inline int san_fen()
34 {
35     int l=1,r=maxa*2;
36     while(l+2<r)
37     {
38         int lmid=l+(r-l)/3,
39             rmid=r-(r-l)/3;
40         if(check(lmid)<check(rmid))r=rmid;
41         else l=lmid;
42     }
43     return min(check(l+1),min(check(l+2),check(l)));
44 }
45 signed main()
46 {
47     cin>>n;
48     for(int i=1;i<=n;i++)
49     {
50         scanf("%lld",&yx[i]);
51         ap[i]=a[i]=yx[i]-i;
52         bp[i]=b[i]=yx[i]+i;
53         sa[i]=sa[i-1]+a[i];
54         sb[i]=sb[i-1]+b[i];
55         maxa=max(maxa,yx[i]);
56     }
57     sort(a+1,a+n+1);sort(b+1,b+n+1);
58     for(int i=1;i<=n;i++)
59     {
60         ap[i]=lower_bound(a+1,a+n+1,ap[i])-a;
61         bp[i]=lower_bound(b+1,b+n+1,bp[i])-b;
62     }
63     for(int i=1;i<=n;i++)T[1].insert(bp[i],yx[i]+i,1);
64     for(now=1;now<=n;now++)
65     {
66         T[1].insert(bp[now],-(yx[now]+now),-1);
67         ans=min(san_fen(),ans);
68         T[0].insert(ap[now],yx[now]-now,1);
69     }
70     cout<<ans<<endl;
71     return 0;
72 }
正解AC
 1 #include<bits/stdc++.h>
 2 #define MAXN 100005
 3 #define int long long
 4 #define re register
 5 using namespace std;
 6 double eps=1e-5,T=100,D;
 7 int mid,n,a[MAXN],b[MAXN];
 8 inline int calc(int x)
 9 {
10     for(int i=1;i<=n;i++)b[i]=a[i]+abs(i-x);
11     nth_element(b+1,b+mid,b+n+1);
12     re int now=max(b[mid],max(x,n-x+1)),ans=0;
13     for(int i=1;i<=n;i++)
14     {
15         if(now-abs(i-x)<=0)return 0x7f7f7f7f7f;
16         ans+=abs(now-b[i]);
17     }
18     return ans;
19 }
20 signed main()
21 {
22     srand((unsigned)time(0));
23     cin>>n;
24     mid=(1+n)>>1;
25     for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
26     int ans=0x7f7f7f7f7f7f;
27     int now=(1+n>>1);
28     int nowans=calc(now),tmp,tmpans;
29     while(T>eps)
30     {
31         tmp=now+(rand()*2ll-RAND_MAX)*T*0.000001;
32         if(T<1.0)tmp=max(tmp,1ll),tmp=min(tmp,n);
33         else tmp=(tmp%n+n)%n+1;
34         tmpans=calc(tmp);D=-fabs(tmpans-nowans);
35         if(tmpans<nowans||exp(D/T)*RAND_MAX>rand())nowans=tmpans,now=tmp;
36         if(tmpans<ans)ans=tmpans;
37         T*=0.975;
38     }
39     cout<<ans<<endl;
40     return 0;
41 }
退火AC

模拟退火是一个特别优秀的算法,请不要针对算法,(尽管你可以针对脸某)。

猜你喜欢

转载自www.cnblogs.com/hzoi-kx/p/11343580.html