noip 2015 day2解题报告
来源:互联网 发布:淘宝店铺号申请 编辑:程序博客网 时间:2024/05/16 18:07
写在前面
因为本解题报告采用了许多大佬的题解与代码,所以记录不过来便设置为转载,如果侵犯了大佬的权益请联系我(也就是转侵删)
跳石头
题目描述
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终 点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达 终点 为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。
思路
二分答案即可
代码:#include <cstdio>#include <cstring>using namespace std;const int maxn=50005;int a[maxn],n,m,ans;bool check(int x){ int sum,last; sum=0;last=0; for (int i=1;i<=n;i++) { if (a[i]-last<x) { sum+=1;continue;}//如果小于就移走当前的石头 last=a[i]; } if (sum>m) return 0; return 1;}int main(){ int L,R,mid; scanf("%d%d%d\n",&L,&n,&m); for (int i=1;i<=n;i++) scanf("%d\n",&a[i]); n+=1; a[n]=L;R=L; ans=L=0; while (L<=R) { mid=L+(R-L)/2; if (check(mid)) { ans=mid; L=mid+1; } else R=mid-1; }//二分过程 printf("%d",ans); return 0;}
子串
1. 思路 首先讲一种最简单的DP吧(也是我最容易看懂的),不过在洛谷上似乎有更优解 我们令f[i][j][k][0/1]表示A串用了前i个字符,B串已覆盖前j个字符,目前为止已经选了k个子串,最后的0/1表示A串的这个字符选了没有(0没选,1选了)。 为了得出状态转移方程,我们分情况讨论:先看f[i][j][k][1](当前位选了),显然当且仅当a[i]=b[j]的时候它才有意义,否则f[i][j][k][1]=0。到这个状态有三种方法:上一位没有选,新开一个子串 上一位选了,延续这个子串上一位选了,但是仍然新开一个子串因此,我们有f[i][j][k][1]=f[i-1][j-1][k-1][0]+f[i-1][j-1][k][1]+f[i-1][j-1][k-1][1]。 状态转移方程中的三项分别对应上述三种情况。注意,因为我们规定了A的这一位必须选(因为状态的最后一维是1),所以所有前驱状态一定是f[i-1][j-1][…][…]。然后讨论另一种情况:这个字符不选。这个比较简单,到这个状态有两种方法:上一位没有选,现在仍然不选上一位选了,结束这个子串 因此,我们有f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1]。合起来就是f[i][j][k][1]=f[i-1][j-1][k-1][0]+f[i-1][j-1][k][1]+f[i-1][j-1][k-1][1](a[i]=b[j])f[i][j][k][1]=0(a[i]!=b[j])f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1]状态转移方程有了,边界也容易确定:f[0][0][0][0]=1。至于最终答案,显然是f[n][m][k][0]+f[n][m][k][1]然后用滚动数组压掉一维就可以
代码:#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int N=1005,M=205,K=205,MOD=1000000007;int n,m,p;char a[N],b[M];int f[2][M][K][2],pre=1,now=0,ans=0;void dp(){ //因为我们发现i时的值只由i-1传递过来,所以只需要用pre,now来定义i-1,i; f[pre][0][0][0]=f[now][0][0][0]=1;//初始化 for(int i=1;i<=n;i++,swap(now,pre))//把现在的now数组存i,pre为i-1; for(int j=1;j<=m;j++) for(int k=1;k<=p;k++){ if(a[i]==b[j]) f[now][j][k][1]=(f[pre][j-1][k-1][0]+f[pre][j-1][k][1])%MOD; else f[now][j][k][1]=0; f[now][j][k][0]=(f[pre][j][k][0]+f[now][j][k][1])%MOD;//DP主体; }}int main() { scanf("%d%d%d%s%s",&n,&m,&p,a+1,b+1); dp(); cout<<f[pre][m][p][0]; return 0;}
运输计划
- 题意
给你一棵树,有很多条路径,让你把一条边的边权设为0,使路径的最大值最小 - 思路
lca+二分
大概思路就是首先二分一个答案(时间),然后对各个计划进行遍历求值,记录时间大于答案的路线并维护最大差值sum,然后寻找这几条路线里有没有公共边且使最长公共边长度变为0后是否合法,不合法就把时间扩大,合法就缩小
(思路确实简单)
/*算法:tarjan + 二分思路:先找lca,可以在dfs的时候就用tarjan直接算出,然后找出s -> t的路程,即 q[i].dis = deep[q[i].from] + deep[q[i].to] - 2* deep[q[i].lca];继续进行二分,对于路程大于假定答案的就cnt++,不然就break,因为提前排过序,最后发现只能对于cnt==num[i]的点进行虫洞操作,最后如果大于就继续缩小,反之增大上限。c++代码如下:#include<iostream>#include<cstring>#include<algorithm>#include<cstdio>#include<queue>#include<stack>using namespace std;const int N = 3e5 + 1;int head[N],f[N],deep[N],v[N],num[N],headm[N],tot,n,m;inline void read(int&x){ x=0;char c;int sign=1; do{ c=getchar();if(c=='-') sign=-1;}while(c<'0'||c>'9'); do{ x=x*10+c-'0';c=getchar();}while(c<= '9'&&c>='0'); x *= sign;}//读入优化 struct node{ int to,next,val;}edge[N*4];//结构体 struct str{ int from,to,lca,dis;}q[N];void add(int head[],int a,int b,int val){ edge[tot].next=head[a]; edge[tot].to=b; edge[tot].val=val; head[a]=tot++;}inline int get_fa(int x) { return x==f[x]?x:f[x]=get_fa(f[x]);}//找爸爸(你的就不用找了,因为就是我[滑稽]) void dfs(int u,int fa){ f[u]=u; for(int i=headm[u];~i/*这表示i=-1就结束因为初始化时数组定的是-1(第87行)*/;i=edge[i].next) { node &e=edge[i];//其实觉得这一段没这么必要,不过简洁了些; if(f[q[e.to].from]&&u==q[e.to].to) q[e.to].lca=get_fa(q[e.to].from); if(f[q[e.to].to]&&u==q[e.to].from) q[e.to].lca=get_fa(q[e.to].to);//求LCA } for(int i=head[u];~i;i=edge[i].next) { node&e=edge[i]; if(e.to==fa) continue; deep[e.to]=deep[u]+e.val;//更新深度 dfs(e.to,u); v[e.to]=e.val; } f[u]=fa;//确立父子关系 }const bool cmp(str a,str b){ return a.dis>b.dis;}void find(int u ,int fa){ for(int i=head[u];~i;i=edge[i].next) { node&e=edge[i]; if(e.to==fa)continue; find(e.to,u); num[u]+=num[e.to]; }}//标记上推; bool check(int x){ int cnt=0,dec=0; memset(num,0,sizeof(num));//清零标记; for(int i=1;i<=m;i++) if(q[i].dis>x){cnt++;dec=max(dec,q[i].dis-x);num[q[i].from]++;num[q[i].to]++;num[q[i].lca]-=2;} //处理非法路,因为待会标记会上推所以最近公共祖先先-=2,这样等下推的时候最近公共祖先的数组值仍会为0,就不会影响结果了; else break; find(1,1); for(int i=1;i<=n;i++) if(cnt==num[i]&&v[i]>=dec)return 1;//如果删去的这个路的值比最大差值大就return 1否则return 0; return 0;}int main(){ memset(head,-1,sizeof(head));memset(headm,0,sizeof(headm));//初始化数组; read(n);read(m); int u,v,val; for(int i=1;i<n;i++) { read(u);read(v);read(val); add(head,u,v,val);add(head,v,u,val);//边数组 } for(int i=1;i<=m;i++) read(q[i].from),read(q[i].to),add(headm,q[i].from,i,0),add(headm,q[i].to,i,0);//询问数组; dfs(1,1);//也就是所谓的trajan; int l=0,r=0,num=0; for(int i=1;i<=m;i++){q[i].dis=deep[q[i].from]+deep[q[i].to]-2*deep[q[i].lca];r=max(q[i].dis,r);}//求最短路; sort(q+1,q+1+m,cmp);//将路径从大到小排序便于进行 while(l<=r) { int mid=(l+r)>> 1; if(check(mid))num=mid,r=mid-1; else l=mid+1; }//二分主体; cout<<num<<endl; return 0;}
这里算是个人的一些感想???
1.这几道题目可是折磨了我好久(主要第三道题,也体会到了大佬们的超神)
2.找题解的过程里发现好多大佬的网页是真的可爱(雾?),我也最喜欢去看这样的题解,因为看起来不是冷冰冰的,运用到与人交流上或许也是如此。
3.发现大佬的思路跟我完全是不同的,比喻为走路的话我或许是想着怎么走到,而大佬却考虑
的是怎么快点走到,所以有好多优化啊那种东西完全看不懂,这或许也就级别的差距吧,我也要好好的加强自己了,至少要追到能看到大佬的背影(%%%)
阅读全文
0 0
- NOIP 2015 Day2 解题报告
- noip 2015 day2解题报告
- [REVIEW] NOIP 2015 Day2 解题报告
- NOIP 2015 提高组 day2 解题报告
- NOIP 2015 Day2 解题报告(全面)
- NOIP 2016 Day2 解题报告
- [2011noip day2]7.27test解题报告
- [解题报告] NOIP 2014 提高组Day2试题
- Day2解题报告
- TJOI2015 Day2解题报告
- NOIP2015 day2 解题报告
- CH Round #49 - Streaming #4 (NOIP模拟赛Day2) 解题报告
- [REVIEW] NOIP 2015 Day1 解题报告
- 【Noip 2015】 子串 解题报告
- [NOIP 2012]解题报告
- NOIP解题报告
- 2008Noip解题报告
- NOIP 2013 解题报告
- 卷积神经网络笔记
- DTS和PTS的解释
- lbrary not found for lWAP 问题解决
- Github访问速度慢-----win10下
- JSON的数据格式
- noip 2015 day2解题报告
- 两点之间往复运动 仿照itween 的pingpong运动
- 大话设计模式之装饰模式
- Spring学习(九)-AOP切面通知
- nyoj 15 括号匹配(二)
- Hadoop5-用MapReduce写流量求和
- 关于MySQL的几点安全配置
- redis4.0.1主从配置及Sentinel配置自动Failover
- Linux下的I/O复用与epoll详解