10.6离线赛
来源:互联网 发布:周防尊cos淘宝 编辑:程序博客网 时间:2024/06/05 11:01
预分230 实分180
构造序列
应得100 实得100
题意:求满足相邻两个数AB,A<=B||A%B!=0的序列的个数,元素范围是[1,k],对1e9+7取模
数据:对于80%,n∈[1,10],k∈[1,1000]
对于100%,n∈[1,10],k∈[1,100000]
一看就是dp题,一层层推过来。如果按照题意直接写过来,那就是O(N * K * K),只能过80%。把条件取反变成(A>B && A%B==0),那就简单了,只需每次转移时把上一层全部的和加上,再把倍数减去即可,这样近似于O(N*K)
#include<bits/stdc++.h>#define Mod 1000000007#define M 100005#define ll long longusing namespace std;ll dp[15][M],cnt[15];int main(){ int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=k;i++)dp[1][i]=1; cnt[1]=k;//记录这一层的总和 for(int i=2;i<=n;i++){//把和转移过来 for(int j=1;j<=k;j++){ dp[i][j]=cnt[i-1]; cnt[i]=(cnt[i]+cnt[i-1])%Mod; } for(int j=1;j<=k;j++) for(int l=j+j;l<=k;l+=j){//把倍数去掉 dp[i][j]=(dp[i][j]-dp[i-1][l]+Mod)%Mod; cnt[i]=(cnt[i]-dp[i-1][l]+Mod)%Mod; } } printf("%lld\n",cnt[n]); return 0;}
1/2背包
应得60 实得60
题意:背包问题,体积仅有1,2两种情况
数据:对于60%,n∈[1,2000],V∈[1,10000]
对于100%,n∈[1,200000],V∈[1,500000]
对于60%,背包问题直接写就可以了。如果不考虑dp的话,就可以直接用贪心解。因为只有1和2两种体积,那么就分成两个数组,一个装1,一个装2,那么只要枚举1的个数,那2也就知道了,排个序就这道题就没了。
可惜我考试时专注于如何dp,然后就没了。
#include<bits/stdc++.h>#define M 500005using namespace std;int A1[M],A2[M],cnt1[M],cnt2[M];bool cmp(int x,int y){return x>y;}int main(){ int n,V; scanf("%d%d",&n,&V); int n1=0,n2=0; for(int i=1;i<=n;i++){ int x,y; scanf("%d%d",&x,&y); if(x==1)A1[++n1]=y; else A2[++n2]=y; } sort(A1+1,A1+1+n1,cmp);sort(A2+1,A2+1+n2,cmp); for(int i=1;i<=V;i++)cnt1[i]=cnt1[i-1]+A1[i]; for(int i=1;i<=V;i++)cnt2[i]=cnt2[i-1]+A2[i]; //注意cnt要转移到很后面,不然若V-2*i>n1,那就错了 //也可以在循环里取min,那样更快 int ans=0; for(int i=0;i<=n2;i++){ if(V-2*i<0)break; int res=cnt2[i]+cnt1[V-2*i]; if(res>ans)ans=res; } printf("%d\n",ans); return 0;}
引水入城
noip2010提高组复赛最后一题
应得70 实得20
题意:给出一个n*m的矩阵表示每一个地方的海拔。只有第一行的颗一建造蓄水池,其他地方只能建造输水池。求能否有一种方案是第n行都能有水。若不行,输出0和有几个地方不可能有水;若行,输出1和至少建几个蓄水池。
数据:如下表
因为数据很详细,水分就很容易。
对于第1、2、3三组数据,因为不能,意味着所有蓄水池都得建,那么只要dfs一下就可以了。但是,输出的是不可能建的,而不是能建的,就这样少了30.
对于第4、5、6三组数据,因为m<=20,直接深搜,但是我只有20分。
对于第7组数据,可以用bitset加四次方的dp过,但我又错了。
正解之前要了解一个前提:如果可以,那么一个蓄水站所能到达的地方就是一段连续的区间。反证法:如果不是一段连续的区间,那么中间断开的地方海拔就比两边要高,那么这个地方就永远到不了。
这样的话用dfs先算出每一个地方对应的连续区间,然后用dp写就行了。这个dp是二次的,转化一下就是求小区间覆盖大区间所需要的个数
#include<bits/stdc++.h>using namespace std;int A[505][505],sum,n,m,dp[505];bool mark[505][505],G[505],Q[505];struct node{int l,r;node(){l=501;r=0;}}a[505];int dx[]={0,1,0,-1};int dy[]={1,0,-1,0};void f(int x,int y,int k){//深搜求所覆盖的区间 mark[x][y]=1; if(x==n){ Q[y]=1; a[k].l=min(a[k].l,y); a[k].r=max(a[k].r,y);//更新 } for(int i=0;i<4;i++){ int X=x+dx[i],Y=y+dy[i]; if(mark[X][Y]||X<1||X>n||Y<1||Y>m)continue; if(A[x][y]>A[X][Y])f(X,Y,k); }}void Init(){ for(int i=1;i<=m;i++){ memset(mark,0,sizeof mark); if(A[1][i]>=A[1][i-1]&&A[1][i]>=A[1][i+1])f(1,i,i); } for(int i=1;i<=m;i++)if(Q[i])sum++;//sum是全部都建所覆盖的城市数}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)scanf("%d",&A[i][j]); Init(); if(sum<m){printf("0\n%d\n",m-sum);return 0;} memset(dp,127,sizeof(dp)); dp[0]=0; for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) if(a[j].l<=i&&a[j].r>=i) dp[i]=min(dp[i],dp[a[j].l-1]+1); printf("1\n%d\n",dp[m]); return 0;}
以后做题不能看到是像什么类型的,就往一个地方想,可能改变了一些细节用其他方法写就更快了。就像第二题不是背包,而是贪心
- 10.6离线赛
- 10.26离线赛题解
- 天池离线赛
- 天池离线赛
- 天池离线赛
- 天池离线赛
- 10.3离线赛
- 10.4离线赛
- 离线赛20171004总结
- 离线赛20171006总结
- 20171006离线赛总结
- 20171007离线赛总结
- 离线赛20171007总结
- 10.7离线赛
- 10.8离线赛
- 离线赛20171008总结
- 离线赛20171008总结
- 离线赛总结
- Linux系统常识(了解)
- 清除一些标签自带的js效果
- linux运行级别的查看与修改
- 自定义classloader实现JAVA热替换
- 2017.10.06【NOIP提高组】模拟赛B组 青蛙 题解
- 10.6离线赛
- POJ 1815 Friendship (最小点割集/最小割)
- linux系统基本命令之进程管理
- MyEclipse导入jquery-1.8.0.min.js等文件报错的解决方案
- 关于递推的简单思想(通过阶乘和斐波那契程序领悟)
- Network Switch vs. Network Hub: What’s The Difference?
- 今天学习css3 position
- some tips about python Six
- PHP基础学习day6(小技巧+抽象类)