首页 > 代码库 > 2017/8/2 考试吐槽

2017/8/2 考试吐槽

2017 8 2 得分:70

首先扯点题外话:之前那么多天考试从来不公开吐槽,主要是因为每天都有我们的学长出的题,涉及到了他们的知识产权,我不方便发出来。但是今天!没有!全是原题!因此我要向全世界吐槽一发!让全世界感受到我的蒟蒻!(滑(fa)稽(gi))好了不废话了,现在进入正题:吐槽+题解……

一句话:代码长度与正确性一定成反比!

A、路面修整

吐槽:果然是我太弱了,这个东西都想不出来……

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1592

题意:求把一个序列修改成非上升或非下降序列最小花费。

$SBDP$,但我并不会表示状态……┑( ̄Д  ̄)┍考试XJBD拿了40分细软跑就滚粗了……

正解也没啥说的,就是SBDP,下面仅以求不下降序列花费为例。设$f[i][j]$为走到$i$最小花费为$j$(由于数据太大,高度需要离散化),那么,$f[i][j]=min{f[i-1][k]+abs(Hash[j]-Hash[a[k]])|1<=k<=j}$(Hash是离散化数组的名字)。但是我们会发现这么转移是$O(n^3)$,需要我们做出显而易见然而你就是没想到的优化:开一个数组存上一轮到$j$为止的最小花费,然后乱搞就可以了……

技术分享
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cstdlib>
 6 using namespace std;
 7 const int maxn=2005;
 8 int n,a[maxn],Hash[maxn],f1[maxn][maxn],cnt,prefix[maxn],f2[maxn][maxn],suffix[maxn];
 9 void Lisan()
10 {
11     sort(Hash+1,Hash+n+1);cnt=unique(Hash+1,Hash+n+1)-Hash-1;
12     for(int i=1;i<=n;i++)a[i]=lower_bound(Hash+1,Hash+cnt+1,a[i])-Hash;
13 }
14 int haha()
15 {
16     //freopen("grading.in","r",stdin);
17     //freopen("grading.out","w",stdout);
18     scanf("%d",&n);
19     for(int i=1;i<=n;i++)scanf("%d",&a[i]),Hash[i]=a[i];
20     Lisan();
21     memset(f1,0x7f,sizeof(f1));
22     memset(f2,0x7f,sizeof(f2));
23     for(int i=1;i<=cnt;i++)f1[0][i]=f2[0][i]=0;
24     for(int i=1;i<=n;i++)
25     {
26         for(int j=1;j<=cnt;j++)
27         {
28             f1[i][j]=prefix[j]+abs(Hash[j]-Hash[a[i]]),f2[i][j]=suffix[j]+abs(Hash[j]-Hash[a[i]]);
29         }
30         for(int j=1;j<=cnt;j++)
31         {
32             prefix[j]=f1[i][j];
33             if(j>1)prefix[j]=min(prefix[j],prefix[j-1]);
34         }
35         for(int j=cnt;j>=1;j--)
36         {
37             suffix[j]=f2[i][j];
38             if(j<cnt)suffix[j]=min(suffix[j],suffix[j+1]);
39         }
40     }
41     int ans1=2147483647,ans2=2147483647;
42     for(int i=1;i<=cnt;i++)ans1=min(ans1,f1[n][i]),ans2=min(ans2,f2[n][i]);
43     printf("%d\n",min(ans1,ans2));
44 }
45 int sb=haha();
46 int main(){;}
A

B、翻格子游戏

吐槽:这不是一个裸的解异或方程组吗……竟然暴力枚举……这种事情让我也很无奈呢……

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1647

题意:翻动一次方块会使该方块以及周围相邻的四个方块上下发生变化,目标是使所有方块反面向上。请输出翻动次数最小的方案中字典序最小的一种。

这题我考试时想的真的是解异或方程组,结果打出来发现自己并不会写=  =……正解出来炸裂了我的神经……

正解竟然是枚举!惊不惊喜!意不意外!开不开心!我们可以看到,$N<=15$,那么我们就可以欢脱的枚举第一排的情况(这个当然可以在$O(2^N)$时间内完成),对于每一个状态,我们在每一行可以再用$O(N)$的时间计算出下一行应翻动的次数和下一行的即时状态。我们可以知道,每一个格子的情况是由总共5个格子决定的,当我们递推到下一行的状态时,前四个已经执行完毕,只剩最后一个还没有决定。于是我们就可以执行令人窒息的操作:上一行翻完后的状态是什么,这一行就怎么翻。$M$行全部检查完成后,检查最后一行,因为最后一行无法再翻。如果合格,计算翻动次数,合适就复制矩阵,然后,就没有然后了……

 

技术分享
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 const int maxn=20;
 7 int n,map[maxn][maxn],effort[maxn][maxn],result[maxn][maxn],tmp[maxn][maxn],m,ans=0x7fffffff;
 8 void solve(int val)
 9 {
10     memset(tmp,0,sizeof(tmp));
11     int l=val;
12     for(int i=0;val;val>>=1,i++)tmp[1][m-i]=val&1;
13     for(int i=1;i<=m;i++)
14     {
15         result[1][i]=map[1][i]^tmp[1][i];
16         if(i>1)result[1][i]^=tmp[1][i-1];
17         if(i<m)result[1][i]^=tmp[1][i+1];
18     }
19     for(int i=2;i<=n;i++)
20     {
21         for(int j=1;j<=m;j++)
22             if(result[i-1][j]!=0)
23             {
24                 tmp[i][j]=1;
25                 result[i-1][j]^=1;
26             }
27         for(int j=1;j<=m;j++)
28         {
29             result[i][j]=map[i][j]^tmp[i][j];
30             result[i][j]^=tmp[i-1][j];
31             if(j>1)result[i][j]^=tmp[i][j-1];
32             if(j<m)result[i][j]^=tmp[i][j+1];
33         }
34     }
35     for(int i=1;i<=m;i++)
36         if(result[n][i])return;
37     int temp=0;
38     for(int i=1;i<=n;i++)
39         for(int j=1;j<=m;j++)temp+=tmp[i][j];
40     if(temp<ans)
41     {
42         ans=temp;
43         memcpy(effort,tmp,sizeof(tmp));
44     }
45 }
46 void print()
47 {
48     for(int i=1;i<=n;i++)
49     {
50         for(int j=1;j<=m;j++)printf("%d ",effort[i][j]);
51         puts("");
52     }
53 }
54 int haha()
55 {
56     //freopen("fliptile.in","r",stdin);
57     //freopen("fliptile.out","w",stdout);
58     scanf("%d%d",&n,&m);
59     for(int i=1;i<=n;i++)
60         for(int j=1;j<=m;j++)scanf("%d",&map[i][j]);
61     int att=(1<<m),tmp;
62     for(tmp=0;tmp<att;tmp++)solve(tmp);
63     if(ans<0x7fffffff)print();
64     else puts("IMPOSSIBLE");
65 }
66 int sb=haha();
67 int main(){;}
B

C、枪战

吐槽:dalao们丧心病狂的码量……我怀疑我是不是走错片场了?

链接:http://cogs.pro/cogs/problem/problem.php?pid=2487

(咳,不要在意这个题面,cogs上膜法师一大片大家又不是不知道,这不是重点……)

题意:每个人最多枪毙一个人(可以自杀),求最少、最多死了多少人。

考试时只想到了Tarjan……并没有考虑全面……于是……10分全场最高分……GG……

首先我们围观一下大猩猩的挑战纪录

咳,扯远了,接下来我们回来讨论人类的解题方法。首先考虑最多会死多少人,在全图中考虑每个连通分量,大概会有如下三种情况:

1、只有一个点,即自尽——全灭

2、好几个点连环套——只活一个

3、链连着环——只有入度为0的人才能活下来。

分别考虑即可。

接下来我们回来考虑人数最少的情况。首先我们找到所有入度为0的人,将他们压进队列,随后,对于每个人,取出来,跟他们连着的人全灭。然后,这些人死后我们来看这些人瞄着的人是不是没人瞄了,如果是,也进队列。这样操作完成后,剩下的就都是环,死的人一定是环大小的一半。问题就解决了。

有一点需要注意:这道题极限数据1000000,你连点都搜不完,因此需要手动开栈。

技术分享
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 const int maxn=1000005;
 7 int belong[maxn],dfn[maxn],low[maxn],scnt,cnt,size[maxn],n,minn,maxx,nxt[maxn];
 8 #include<stack>
 9 stack<int>s;
10 void dfs(int u)
11 {
12     low[u]=dfn[u]=++cnt;
13     s.push(u);
14     int v=nxt[u];
15     if(!dfn[v])
16     {
17         dfs(v);
18         low[u]=min(low[u],low[v]);
19     }
20     else if(!belong[v]) low[u]=min(low[u],dfn[v]);
21     if(low[u]==dfn[u])
22     {
23         int k;scnt++;
24         do
25         {
26             k=s.top();s.pop();
27             belong[k]=scnt;size[scnt]++;
28         }while(k!=u);
29     }
30 }
31 int entrance_degree[maxn],die[maxn],vis[maxn];
32 #include<queue>
33 queue<int>q;
34 int main()
35 {
36     freopen("maf.in","r",stdin);
37     freopen("maf.out","w",stdout);
38     int __size__= 128 << 20;
39     char *__p__ = (char*)malloc(__size__) + __size__;
40     __asm__("movl %0, %%esp\n" :: "r"(__p__));
41     scanf("%d",&n);
42     for(int i=1;i<=n;i++)
43     {
44         scanf("%d",&nxt[i]);
45         die[nxt[i]]=1;
46         entrance_degree[nxt[i]]++;
47     }
48     for(int i=1;i<=n;i++)
49         if(!die[i])
50         {
51             q.push(i);
52             vis[i]=1;
53         }
54     while(!q.empty())
55     {
56         int u=q.front();q.pop();vis[u]=1;
57         if(!vis[nxt[u]])
58         {
59             vis[nxt[u]]=die[nxt[u]]=1;minn++;
60             if(!(--entrance_degree[nxt[nxt[u]]]))q.push(nxt[nxt[u]]);
61         }
62     }
63     for(int i=1;i<=n;i++)
64         if(!vis[i])
65         {
66             int tmp=0,now=i;
67             while(!vis[now])
68             {
69                 vis[now]=1;
70                 tmp++;
71                 now=nxt[now];
72             }
73             minn+=(tmp+1)>>1;
74         }
75     for(int i=1;i<=n;i++)
76         if(!dfn[i])dfs(i);
77     memset(entrance_degree,0,sizeof(entrance_degree));
78     for(int i=1;i<=n;i++)
79         if(belong[i]!=belong[nxt[i]]||i==nxt[i])entrance_degree[belong[nxt[i]]]++;
80     for(int i=1;i<=scnt;i++)
81         if(!entrance_degree[i])maxx++;
82     printf("%d %d\n",minn,n-maxx);
83 }
C

成绩嘛……我就不贴了……你们都看到了不是吗?(逃)

去颓下一篇博文去了

2017/8/2 考试吐槽