NOIP2015提高组解析
来源:互联网 发布:板式家具软件 编辑:程序博客网 时间:2024/06/06 02:08
题目见此
day1
神奇的幻方:
裸裸的模拟(其实也可以发现规律:i+1在i的右上方,如果已经有数了,就填在i的下方)
参考程序:
#include<cstdio>#include<algorithm>using namespace std;int a[50][50];int nx,ny,n;int main(){freopen("magic.in","r",stdin);freopen("magic.out","w",stdout);scanf("%d",&n);nx=1;ny=n/2+1;a[nx][ny]=1;for (int i=2;i<=n*n;i++){int tx,ty;if (nx==1){if (ny!=n){tx=n;ty=ny+1;}else{tx=nx+1;ty=ny;}}else{if (ny==n){tx=nx-1;ty=1;}else{tx=nx-1;ty=ny+1;if (a[tx][ty]){tx=nx+1;ty=ny;}}}a[tx][ty]=i;nx=tx;ny=ty;}//紧跟题目走,比较low的程序for (int i=1;i<=n;i++){for (int j=1;j<=n;j++)printf("%d ",a[i][j]);printf("\n");}return 0;}
信息传递:
当时比赛时用Pascal打了个tarjan求强连通分量,不知为什么爆栈40分,心痛心痛
所以这次打了个简单的深搜,其实就是沿着走,如果当前的点已经访问过了就是一个环。
参考程序:
#include<cstdio>#include<algorithm>#include<cstring>#include<ctime>#define maxn 210000using namespace std;int a[maxn];int ans[maxn];int res=0x7f7f7f7f;bool vis[maxn];int n;void dfs(int x,int k){ans[x]=k;vis[x]=true;if (!vis[a[x]])dfs(a[x],k+1);if (ans[a[x]] && ans[x]-ans[a[x]]+1)res=min(res,ans[x]-ans[a[x]]+1);ans[x]=0;}int main(){freopen("message.in","r",stdin);freopen("message.out","w",stdout);scanf("%d",&n);memset(vis,0,sizeof(vis));memset(ans,0,sizeof(ans));for (int i=1;i<=n;i++)scanf("%d",&a[i]);for (int i=1;i<=n;i++)if (!vis[i])dfs(i,1);printf("%d",res);//printf("%d\n%.2f",res,(double)clock()/CLOCKS_PER_SEC);return 0;}
斗地主:
又是深搜(好像几年的NOIP的压轴题都是倍增和深搜。。)
其实,每次深搜时只需记录当前的步数和剩下的牌,对于每一个状态首先模拟不打顺子的情况最少步数(无需深搜,直接计算),再深搜需要打顺子的情况,加个最优化剪枝就行了,不是非常复杂。
参考程序:
#include<cstdio>#include<algorithm>#include<cstring>using namespace std;int n;int hand[25];int sum[25];int ans;int unline(){memset(sum,0,sizeof(sum));for (int i=0;i<=13;i++)sum[hand[i]]++; int tot=0; while(sum[4]&&sum[2]>1) sum[4]--,sum[2]-=2,tot++; while(sum[4]&&sum[1]>1) sum[4]--,sum[1]-=2,tot++; while(sum[4]&&sum[2]) sum[4]--,sum[2]--,tot++; while(sum[3]&&sum[2]) sum[3]--,sum[2]--,tot++; while(sum[3]&&sum[1]) sum[3]--,sum[1]--,tot++; return tot+sum[1]+sum[2]+sum[3]+sum[4]; }void dfs(int dep){if (dep>=ans)return;int tmp=unline();if (tmp+dep<ans)ans=tmp+dep;for (int i=2;i<=13;i++) { int j=i;while(hand[j]>=3) j++; if(j-i>=2) { for (int j2=i+1;j2<=j-1;j2++) { for (int k=i;k<=j2;k++) hand[k]-=3; dfs(dep+1); for (int k=i;k<=j2;k++) hand[k]+=3; } } } for (int i=2;i<=13;i++) { int j=i;while(hand[j]>=2) j++; if(j-i>=3) { for (int j2=i+2;j2<=j-1;j2++) { for (int k=i;k<=j2;k++) hand[k]-=2; dfs(dep+1); for (int k=i;k<=j2;k++) hand[k]+=2; } } } for (int i=2;i<=13;i++) { int j=i;while(hand[j]>=1) j++; if(j-i>=5) { for (int j2=i+4;j2<=j-1;j2++) { for (int k=i;k<=j2;k++) hand[k]--; dfs(dep+1); for (int k=i;k<=j2;k++) hand[k]++; } } }}int main(){freopen("landlords.in","r",stdin);freopen("landlords.out","w",stdout);int T;scanf("%d%d",&T,&n);while (T--){memset(hand,0,sizeof(hand));for (int i=1;i<=n;i++){int x,y;scanf("%d%d",&x,&y);if(x==1) x=13; else if(x) x--; hand[x]++;}ans=0x7f7f7f7f;dfs(0);printf("%d\n",ans);}return 0;}
day2:
跳石头:
此题在poj之旅中应做过,二分即可,比赛当时因为r上限取错了,30分没了,心痛心痛。
建立模型(l,r]区间,取mid贪心判断,(贪心:如果当前石头比上一个选取剩下的石头的距离小于mid就把当前石头移走,最后若移走的石头数大于m,就说明mid过大,r=mid,否则l=mid。
参考程序:
#include<cstdio>#include<algorithm>#define maxn 110000using namespace std;int n,m,L;int a[maxn];bool check(int k){int j=0,cnt=0;for (int i=1;i<=n;i++)if (a[i]-a[j]<k)cnt++;else j=i;return cnt<=m;}int main(){freopen("stone.in","r",stdin);freopen("stone.out","w",stdout);scanf("%d%d%d",&L,&n,&m);for (int i=1;i<=n;i++)scanf("%d",&a[i]);a[0]=0;a[n+1]=L;int l=0,r=L+1;while (l+1<r){int mid=(l+r)>>1;if (check(mid))l=mid;else r=mid;}printf("%d",l);return 0;}
子串:
其实就是dp,令f[i][j][k]表示A串选到第i个位置(第i个字母要选),B串选到第j个位置,共用了k个子串的方案数
则若a[i]!=b[j] , f[i][j][k]=0;
a[i]==b[j],f[i][j][k]=f[i-1][j-1][k](和前一个连成一个子串)+f[p][j-1][k-1](0<p<=i)
显然,既超时又超空间。
对于f[p][j-1][k-1]求和,我们预处理sum[i][j-1][k-1]=f[1][j-1][k-1]+.....f[i][j-1][k-1],于是只需O(1)时间转移,总效率O(nmk)
再发现f[][][k]只于f[][][k-1]和sum[][][k-1]有关,滚动数组即可。
虽然已满分,但是显然很麻烦,
我们重新定义f[i][j][k][0]表示A串选到第i个位置(第i个字母不选),B串选到第j个位置,共用了k个子串的方案数
f[i][j][k][1]表示A串选到第i个位置(第i个字母要选),B串选到第j个位置,共用了k个子串的方案数
原理同上,可推出(t=i&1,p=1-t---->滚动数组):
f[k][t][j][1]=((f[k-1][p][j-1][0]+f[k-1][p][j-1][1])+f[k][p][j-1][1]);
f[k][t][j][0]=(f[k][p][j][0]+f[k][p][j][1]);
参考程序:
#include<cstdio>#include<algorithm>#include<cstring>using namespace std;const int maxn=1100;const int Mod=1000000007;char a[maxn],b[maxn];int f[210][2][210][2];int sum=0,n,m,K;int main(){freopen("substring.in","r",stdin);freopen("substring.out","w",stdout);scanf("%d%d%d",&n,&m,&K);scanf("%s%s",a+1,b+1);for (int i=1;i<=n;i++){int t=i&1;int p=1-t;f[1][t][1][0]=sum;if (a[i]==b[1])sum++,f[1][t][1][1]=1;for (int j=2;j<=m;j++)for (int k=1;k<=K;k++)if (a[i]!=b[j])f[k][t][j][1]=0,f[k][t][j][0]=(f[k][p][j][0]+f[k][p][j][1])%Mod;else{f[k][t][j][1]=((f[k-1][p][j-1][0]+f[k-1][p][j-1][1])%Mod+f[k][p][j-1][1])%Mod;f[k][t][j][0]=(f[k][p][j][0]+f[k][p][j][1])%Mod;}for (int j=1;j<=m;j++)for (int k=1;k<=K;k++)f[k][p][j][0]=f[k][p][j][1]=0;}printf("%d",(f[K][n&1][m][0]+f[K][n&1][m][1])%Mod);return 0;}
运输计划
此题最好在BZOJ上测,其他oj都会爆栈。
显然直接求最大值不容易,所以我们可以二分结果,取mid
对于一些线路所需时间小于mid的,我们可以不用管,对于大于mid的,应取这些线路的最大公共线段,如果将这一段改为虫洞仍不能满足要求,则mid过小,调整区间,反之同理。
但我们还是需要预处理,以1为根建立剖分树(剖分法求LCA),然后求问题中线路起点终点对应的LCA和无虫洞时所需的时间,然后就是二分了。
参考程序:
#include<cstdio>#include<algorithm>#include<cstring>#define maxn 310000using namespace std;int cf[maxn],X[maxn],Y[maxn];int dis[maxn],d[maxn],D[maxn],fa[maxn];int dep[maxn],u[maxn],v[maxn],w[maxn];int top[maxn],size[maxn],son[maxn];int a[3*maxn];int n,m,NodeCnt=0;struct Node{int j,v,next;}e[3*maxn];void addedge(int u,int v,int w){ int p=++NodeCnt; e[p].j=v;e[p].v=w;e[p].next=a[u]; a[u]=p;}void dfs1(int u){ dep[u]=dep[fa[u]]+1; size[u]=1; int Max=0; for (int p=a[u];p;p=e[p].next) if (e[p].j!=fa[u]){ int j=e[p].j; dis[j]=dis[u]+e[p].v; fa[j]=u; dfs1(j); size[u]+=size[j]; if (size[u]>Max){ Max=size[u]; son[u]=j; } }}void build(int u,int tp){ top[u]=tp; if (son[u])build(son[u],tp); for (int p=a[u];p;p=e[p].next) if (e[p].j!=fa[u] && e[p].j!=son[u])build(e[p].j,e[p].j);}int LCA(int u,int v){ int fu=top[u],fv=top[v]; while (fu != fv){ if (dep[fu]<dep[fv])swap(fu,fv),swap(u,v); u=fa[fu];fu=top[u]; } return dep[u]<dep[v]?u:v;}int redu=0,tot=0;int cnt[maxn];int dfs2(int u){for (int p=a[u];p;p=e[p].next)if (e[p].j != fa[u])cnt[u]+=dfs2(e[p].j);if (cnt[u]==tot)redu=max(redu,d[u]);return cnt[u];}bool check(int mid){memset(cnt,0,sizeof(cnt));int Max=0;tot=redu=0;for (int i=1;i<=n;i++)if (D[i]>mid){Max=max(Max,D[i]);cnt[X[i]]++;cnt[Y[i]]++;cnt[cf[i]]-=2;tot++;}dfs2(1);return Max-redu<=mid;}int main(){ freopen("transport.in","r",stdin); freopen("transport.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<n;i++){ scanf("%d%d%d",&u[i],&v[i],&w[i]); addedge(u[i],v[i],w[i]); addedge(v[i],u[i],w[i]); } dfs1(1); build(1,1); for (int i=1;i<n;i++){ if (dep[u[i]]>dep[v[i]])d[u[i]]=w[i]; else d[v[i]]=w[i]; } int l=0,r=0; for (int i=1;i<=m;i++){ scanf("%d%d",&X[i],&Y[i]); r=max(r,D[i]=dis[X[i]]+dis[Y[i]]-2*dis[cf[i]=LCA(X[i],Y[i])]);} while (l<r){ int mid=(l+r)>>1; if (check(mid))r=mid; else l=mid+1; }printf("%d",l); return 0;}
- NOIP2015提高组解析
- NOIP2015提高组 总结
- Noip2015提高组总结
- NOIP2015提高组Day1
- NOIP2015提高组 总结&反思
- NOIp2015提高组 解题报告
- {小结}NOIP2015提高组Day1
- 【NOIP2015提高组】跳石头
- NOIP2015提高组Day1 Message
- NOIp2015 提高组 信息传递
- NOIP2015提高组简单题
- NOIP2015提高组解题报告
- NOIP2015提高组 信息传递
- NOIP2015提高组 跳石头
- NOIP2015提高组模拟8.12总结
- NOIP2015 提高组 day1 信息传递
- NOIP2015提高组复赛 解题报告
- NOIP2015提高组Day2 子串
- 【补习】二维数组,指针,函数
- Scrapy定向爬虫教程(五)——保持登陆状态
- 玩树莓派(1)
- [转]Eclipse连接SQL Server 2008数据库
- VS2008:无法执行添加/移动操作,代码元素是只读的
- NOIP2015提高组解析
- Android基础控件——CardView的使用、仿支付宝银行卡
- 记录基础学习第二_小项目_飞行棋小游戏
- eclipse没有Dynamic web project选项
- 内存申请与释放
- 洛谷 P1058 立体图
- 瀑布布局流--原生js
- 1613-3-傅溥衍 总结《2016年10月15日》【连续第十五天总结】
- myeclipse中安装jrebel插件