NOIP2017模拟赛(六)总结

来源:互联网 发布:床上用品 知乎 编辑:程序博客网 时间:2024/05/20 04:13

NOIP2017模拟赛(六)解题报告

T1:

巧克力铁丝

题目描述

在一个二维平面里,有n块巧克力,每块巧克力都是长方形(正方形也可以认为是长方形),每块巧克力的四条边都平行于X轴或平行于Y轴。我们用(X1, Y1,X2, Y2)来描述一块巧克力的所在位置,其中(X1, Y1)表示这块巧克力左下角的坐标,(X2,Y2) 表示这块巧克力右上角的坐标。注意:题目给出的n块巧克力之间可能有重叠的地方。奶牛bessie手头上有一个a × b的长方形铁丝框. Bessie想知道它应该把铁丝框放在哪个位置,才能使得可以拿走的巧克力的个数最多?农夫FJ规定:bessie铁丝框放的位置也必须要平行X轴和Y轴,而且还规定,bessie只能拿走在铁丝框里面的巧克力,bessie最多能拿走多少块巧克力?bessie只能放一次铁丝框。解释:如果某块巧克力的位置是:(1,1, 2, 2), 而铁丝框的位置是(-1,1,2,100), 那么这块巧克力也是在铁丝框里面,可以被bessie拿走。也就是说,如果某块巧克力任何部分都没有超出铁丝框, 就可以认为是在铁丝框里面。

输入格式 1869.in

第一行:一个整数n. 0 <= n <= 50
第2至n+1行,每行四个整数: X1, Y1, X2, Y2, 描述巧克力的位置. -10^9 <= X1,Y1, X2, Y2 <= 10^9. 
最后一行:两个整数: a 、b. 且1 <= a , b <= 10^9.

输出格式 1869.out

一个整数,bessie最多能拿走多少块巧克力?

输入样例 1869.in

样例1输入: 
3
1 1 2 2
2 2 3 3
3 3 4 4
2 2
样例2输入:
2
0 1 2 3
3 0 4 2
4 3

输出样例 1869.out

样例1输出:

样例解释:如果bessie把铁丝框放在(1,1,3,3)处, 那么它可以拿走第1和第2块巧克力;如果把铁丝框放在(2,2,4,4)那么它可以拿走第2和第3块巧克力。
样例2输出:
2

题目分析:这种比赛的套路好像都是第一题水题呀……我们先枚举2n个x坐标,2n个y坐标,然后以(x_i,y_i)为右上角看看能框住几个矩形就好了。因为最优答案的框法一定可以通过移动,使得它的右上角是某个矩形的x和另一个矩形的y的交点。

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=55;struct data{int lx,dy,rx,uy;} ret[maxn];int X[maxn<<1];int Y[maxn<<1];int n,a,b,ans=0;bool Judge(int alx,int arx,int ady,int auy,int blx,int brx,int bdy,int buy){return alx<=blx && brx<=arx && ady<=bdy && buy<=auy;}int main(){freopen("a.in","r",stdin);freopen("a.out","w",stdout);scanf("%d",&n);for (int i=1; i<=n; i++)scanf("%d%d%d%d",&ret[i].lx,&ret[i].dy,&ret[i].rx,&ret[i].uy);scanf("%d%d",&a,&b);for (int i=1; i<=n; i++){int x=(i<<1)-1;X[x]=ret[i].lx;Y[x]=ret[i].dy;x++;X[x]=ret[i].rx;Y[x]=ret[i].uy;}for (int i=1; i<=(n<<1); i++)for (int j=1; j<=(n<<1); j++){int temp=0;for (int k=1; k<=n; k++)if (Judge(X[i]-a,X[i],Y[j]-b,Y[j],ret[k].lx,ret[k].rx,ret[k].dy,ret[k].uy))temp++;ans=max(ans,temp);temp=0;for (int k=1; k<=n; k++)if (Judge(X[i]-b,X[i],Y[j]-a,Y[j],ret[k].lx,ret[k].rx,ret[k].dy,ret[k].uy))temp++;ans=max(ans,temp);}printf("%d\n",ans);return 0;}

T2:

旅行 

题目描述

在一段时间之后,网络公司终于有了一定的知名度,也开始收到一些订单,其中最大的一宗来自B市。Blue Mary决定亲自去签下这份订单。
为了节省旅行经费,他的某个金融顾问建议只购买U航空公司的机票。U航空公司的所有航班每天都只有一班,并且都是上午出发当天下午到达的,所以他们每人每天只能坐一班飞机。经过调查,他们得到了U航空公司经营的所有航班的详细信息,这包括每一航班的出发地,目的地以及最多能买到的某一天出发的票数。(注意: 对于一个确定的航班,无论是哪一天,他们最多能买到的那一天出发的票数都是相同的。)
Blue Mary注意到他们一定可以只乘坐U航空公司的航班就从A市到达B市,但是,由于每一航班能买到的票的数量的限制,他们所有人可能不能在同一天到达B市。所以现在Blue Mary需要你的帮助,设计一个旅行方案使得最后到达B市的人的到达时间最早。

输入格式 1870.in

第一行包含3个正整数N,M和T。题目中会出现的所有城市分别编号为1,2,…,N,其中城市A编号一定为1,城市B编号一定为N. U公司一共有M条(单向)航班。而连Blue Mary在内,公司一共有T个人要从A市前往B市。
以下M行,每行包含3个正整数X,Y,Z, 表示U公司的每一条航班的出发地,目的地以及Blue Mary最多能够买到的这一航班某一天出发的票数。(即:无论是哪一天,Blue Mary最多只能买到Z张U航空公司的从城市X出发到城市Y的机票。)
输入保证从一个城市到另一个城市的单向航班最多只有一个。

输出格式 1870.out

仅有一行,包含一个正整数,表示最后到达B市的人的最早到达时间。假设他们第一次乘飞机的那一天是第一天。

输入样例 1870.in

3 3 5
1 2 1
2 3 5
3 1 4

输出样例 1870.out

6
本题数据范围
2 <= N <= 50
1 <= M <= 2450
1 <= T <= 50
1 <= X
,Y <= N
X != Y
1 <= Z <= 50

题目分析:先来说说我在考场上的想法吧。这题有图,每条边有流量限制,感觉是网络流,于是我就想网络流了。但好像,直接跑有点怪……虽然想到要二分答案,但不知怎么跑网络流检验。当时时间只剩下40min,我就想贪心检验得了。我在在图中选出一些路径,使它们的流量不超过限制,并且这几天一直走这些路,这样感觉能水很多分(tututu:像这种没有多组数据,给出信息很多最后却只要求输出一个1~100的数的题,是很容易水分的,而且用贪心本来就可以水正解是网络流的题很多分)。我发现二分天数为mid时,一条长度为len,流量为flow的路径,如果我这几天一直走这条路径,它对人数的贡献就是f=(mid-len)*flow,这样我就不停选f值大的路径。我们记f[len][node]表示路径长度为len时,1到达node能得到的最大流量,DP一下就可以了。

然而我只剩下20min敲代码了,老师叫收代码的时候我这题才刚刚编译完,不管了先交了。交了之后我试了下样例,RE?发现有个数组开小了,啊啊啊啊啊好后悔啊!咦?老师还没开始测?我就改了然后过了样例再交上去,覆盖掉我之前的那个文件(至少我认为覆盖掉了)

“老师我又改了一下b那题,你再拿一次源代码吧。”

“嗯。”

我看了看我现在分数:100+0+100=200,第二题又WA又RE的,惨不忍睹。我想我新的代码能拿到分的。老师又拷了一次,再测。

啊?和之前的结果一模一样?

“还是一样嘛。”老师说。

哦,我有些失落,我见到网络流大神tututu第二题AC了,总分290。要是我第二题能拿到分,就不会差距这么大了。也是呢,我第二题才过了样例,说不定又是哪个地方炸了,但我自认为我敲代码一般都很稳的说……我没说什么,回去了。

然后我将代码交到学校OJ上测:80。

这,这不科学。我要投诉!然而老师没鸟我。怎么回事?是老师取文件之前忘了刷新吗,还是我又将旧程序交了上去?(事实上我们学校的ftp好像有点问题,好像放了新的文件上去,有些电脑还是会显示旧文件)

算了,往好的方面想,考场上也没机会给你再交一次嘛……

好了扯了这么多,说回正解吧。

我们二分天数mid,然后将i号点拆成mid个点i_1,i_2……i_mid,表示i号点第mid天的状态,然后如果原图j到i有一条流量为f的边就将新图j_d向i_d+1连一条f的边,然后跑网络流。

听上去很简单嘛。后来我听说这就是网络流24题之中一题的原题。然而像我这种蒟蒻考场上就想不到。明明都已经记了一个f[len][node],居然还没想到拆点……

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=52;const int maxm=2490;const int maxv=6010;const int maxe=500000;struct edge{int obj,cap;edge *Next,*rev;} e[maxe<<1];edge *head[maxv];edge *nhead[maxv];int cur;int level[maxv];int que[maxv];int he,ta;int u[maxm];int v[maxm];int w[maxm];int n,m,T,t;void Add(int x,int y,int f){cur++;e[cur].obj=y;e[cur].cap=f;e[cur].rev=&e[cur+1];e[cur].Next=head[x];head[x]=e+cur;cur++;e[cur].obj=x;e[cur].cap=0;e[cur].rev=&e[cur-1];e[cur].Next=head[y];head[y]=e+cur;}bool Bfs(){for (int i=1; i<=t; i++) level[i]=0,nhead[i]=head[i];he=0,ta=1,que[1]=1,level[1]=1;while (he<ta){int node=que[++he];for (edge *p=head[node]; p; p=p->Next){int son=p->obj;if ( !level[son] && p->cap ){level[son]=level[node]+1;que[++ta]=son;}}}return level[t];}int Dfs(int node,int maxf){if ( node==t || !maxf ) return maxf;int nowf=0;for (edge *&p=nhead[node]; p; p=p->Next){int son=p->obj;if ( level[son]==level[node]+1 && p->cap ){int d=Dfs(son, min(p->cap,maxf) );p->cap-=d;p->rev->cap+=d;maxf-=d;nowf+=d;if (!maxf) break;}}if (maxf) level[node]=0;return nowf;}int Dinic(){int temp=0;while ( Bfs() && temp<=T ){temp+=Dfs(1,T);int h=0;h++;}return temp;}bool Judge(int day){t=n*day+n;cur=-1;for (int i=1; i<=t; i++) head[i]=NULL;for (int d=0; d<day; d++){for (int i=1; i<=m; i++) Add(d*n+u[i],d*n+n+v[i],w[i]);for (int i=1; i<=n; i++) Add(d*n+i,d*n+n+i,T);}return Dinic()>=T;}int Binary(){int L=0,R=maxn<<1;while (L+1<R){int mid=(L+R)>>1;if ( Judge(mid) ) R=mid;else L=mid;}return R;}int main(){freopen("1870.in","r",stdin);freopen("1870.out","w",stdout);scanf("%d%d%d",&n,&m,&T);for (int i=1; i<=m; i++) scanf("%d%d%d",&u[i],&v[i],&w[i]);int ans=Binary();printf("%d\n",ans);return 0;}

T3:

交通违法

题目描述

禅城区有N条双向道路和N个路口,路口的编号从1至N。每条道路连接两个路口。两个路口之间不会有重边。
保证任意两个路口都是相互到达的。现在觉得在一些路口装上摄像头,检测路面的违法情况。
装在路口的摄像头可以监测到所有连接到这个路口的道路。现在的问题是:最少需要在多少个路口安装摄像头才能监测所有的道路?

输入格式 1832.in

输入文件第1行包括一个数字N(1 <= N <= 100000)
接下来N行,第i+1行的第一个数字Ai表示有Ai条道路与路口i相连,后面紧跟着Ai个数字,表示与路口i直接相连的路口。

输出格式 1832.out

最少的摄像头数量。

输入样例 1832.in

3
2 2 3
2 1 3
2 1 2

输出样例 1832.out

2

题目分析:环套树裸题,没什么好说的。如果是一棵树,那么对于一个点,它不选(g[node])它的所有孩子就都要选(f[son]),它选(f[node])它的孩子就可以不选min(f[son],g[son])。现在有个环,我们就将环拆出来,先在每个外向树上跑一次DP,最后再在环上跑DP。

值得注意的是这题的N达到100000,而我们老师是在windows上测的,所以很可能爆栈。于是我就写了个BFS+手工栈,实测100。tututu和fyz因为写了DFS爆栈一个点,只有90。(暗爽)

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=100100;struct edge{int obj;edge *Next;} e[maxn<<1];edge *Head[maxn];edge *nhead[maxn];int cur=-1;bool self=false;int f[maxn];int g[maxn];int fa[maxn];int dep[maxn];int down=0,up;bool on_ring[maxn];int ring[maxn];int Size=0;int que[maxn];int sak[maxn];int head,tail;int N;void Add(int x,int y){cur++;e[cur].obj=y;e[cur].Next=Head[x];Head[x]=e+cur;}void Bfs(int root) //fa[root]=0;{head=0,tail=1;que[1]=root;while (head<tail){int node=que[++head];f[node]=1,g[node]=0;for (edge *p=Head[node]; p; p=p->Next){int son=p->obj;if ( son!=fa[node] && !on_ring[son] ){fa[son]=node;que[++tail]=son;}}}for (int i=tail; i>1; i--){int node=que[i];f[ fa[node] ]+=min(f[node],g[node]);g[ fa[node] ]+=f[node];}}void Find_ring(){for (int i=1; i<=N; i++) nhead[i]=Head[i];tail=1;sak[tail]=1;while (tail){int node=sak[tail];edge *&p=nhead[node];if (!p){tail--;continue;}int son=p->obj;p=p->Next;if (son==fa[node]) continue;if (dep[son]){down=node;up=son;break;}dep[son]=dep[node]+1;fa[son]=node;sak[++tail]=son;}}int main(){freopen("c.in","r",stdin);freopen("c.out","w",stdout);scanf("%d",&N);for (int i=1; i<=N; i++){Head[i]=NULL;int A;scanf("%d",&A);while (A--){int j;scanf("%d",&j);if (i!=j) Add(i,j);else self=true,down=i;}}if (self){Bfs(down); // dont Bfs points on the ring!printf("%d\n",f[down]);}else{dep[1]=1;Find_ring();ring[++Size]=down;while (fa[ ring[Size] ]!=up) Size++,ring[Size]=fa[ ring[Size-1] ];ring[++Size]=up;for (int i=1; i<=Size; i++) on_ring[ ring[i] ]=true;for (int i=1; i<=Size; i++) fa[ ring[i] ]=0,Bfs(ring[i]);for (int i=2; i<=Size; i++)f[ ring[i] ]+=min(f[ ring[i-1] ],g[ ring[i-1] ]),g[ ring[i] ]+=f[ ring[i-1] ];int ans=f[ ring[Size] ];g[ ring[1] ]=maxn;for (int i=2; i<=Size; i++)f[ ring[i] ]+=min(f[ ring[i-1] ],g[ ring[i-1] ]),g[ ring[i] ]+=f[ ring[i-1] ];ans=min(ans,g[ ring[Size] ]);printf("%d\n",ans);}return 0;}

原创粉丝点击