預分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;
}
以後做題不能看到是像什麼類型的,就往一個地方想,可能改變了一些細節用其他方法寫就更快了。就像第二題不是揹包,而是貪心