10.6離線賽

預分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;
}

以後做題不能看到是像什麼類型的,就往一個地方想,可能改變了一些細節用其他方法寫就更快了。就像第二題不是揹包,而是貪心

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章