预分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 long
using 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 500005
using 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;
}
以后做题不能看到是像什么类型的,就往一个地方想,可能改变了一些细节用其他方法写就更快了。就像第二题不是背包,而是贪心