1. 程式人生 > 其它 >7.5 模擬賽

7.5 模擬賽

7.5 模擬賽

DP專題

A 塗色paint

明顯的區間DP

轉移方程:

\[f[i][j]=\begin{cases} max(f[i+1][j],f[i][j-1])\;(c[i]==c[j])\\ max(f[i][k]+f[k+1][j])\;(c[i]!=c[j]) \end{cases} \]

碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
using namespace std;
const int INF=0x3f3f3f3f,N=100010;

inline int read(){
    int x=0,y=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*y;
}

int n,w[N],s[N];
int t,h,q[N];
int f[N],g[N];

int main(){

    n=read();
    for(int i=n;i>=1;i--) w[i]=read();
    for(int i=1;i<=n;i++) s[i]=s[i-1]+w[i];

    h=0,t=0;
    for(int i=1;i<=n;i++){
        while(h<t&&s[q[h+1]]+g[q[h+1]]<=s[i]) ++h;
        f[i]=f[q[h]]+1;
        g[i]=s[i]-s[q[h]];
        while(h<t&&s[q[t]]+g[q[t]]>=s[i]+g[i]) --t;
        q[++t]=i;
    }

    printf("%d\n",f[n]);
    
    return 0;
}

B 粉刷匠

預處理+分組揹包

預處理出每個木板塗k次最多能塗對多少塊,線性DP

用分組揹包,每個木板為一組,每個木板塗多少次是1個單位

選就完了

碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
using namespace std;
const int INF=0x3f3f3f3f;

int n,m,t,mapp[55][2510];
int g[55][2510][55],f[2][2510];

inline int read(){
    int x=0,y=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*y;
}

int main(){

    n=read(),m=read(),t=read();
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
        mapp[i][0]=0;
        char a;cin>>a;
        mapp[i][j]=mapp[i][j-1]+(a=='1');
    }

    for(int i=1;i<=n;i++)
	    for(int j=1;j<=m;j++)
	        for(int k=1;k<=m;k++)
	            for(int q=j-1;q<k;q++)
		            g[i][j][k]=max(g[i][j][k],g[i][j-1][q]+max(mapp[i][k]-mapp[i][q],k-q-mapp[i][k]+mapp[i][q]));//塗0還是塗1

    for(int i=1;i<=n;i++){
        for(int j=1;j<=t;j++){
            for(int k=0;k<=min(m,j);k++){
                int op=(i%2)^1;
                f[i&1][j]=max(f[i&1][j],f[op][j-k]+g[i][k][m]);
            }
        }
    }

    int ans=0;

    for(int i=1;i<=t;i++) ans=max(ans,f[n&1][i]);

    printf("%d\n",ans);
    
    return 0;
}

C 花神的數論題

數位DP板子無壓力

甚至不用記錄前導零和原數

碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
using namespace std;
const ll INF=0x3f3f3f3f,Mod=10000007;

ll n,len,ans=1,a[55];
ll f[55][55];

inline ll read(){
    ll x=0,y=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*y;
}

ll dfs(int pos,int sum1,int lim){
    if(pos>len){
        if(sum1!=0) return sum1%Mod;
        return 1;
    }
    if(!lim&&f[pos][sum1]!=-1) return f[pos][sum1]%Mod;
    ll res=1;
    int ret=lim?a[len-pos+1]:1;
    for(int i=0;i<=ret;i++){
        (res*=dfs(pos+1,sum1+(i==1),(lim&&i==ret)))%=Mod;
    }
    if(!lim) f[pos][sum1]=res;
    return res;
}

ll part(ll x){
    len=0;
    while(x){a[++len]=x&1,x>>=1;}
    memset(f,-1,sizeof f);
    ll res=dfs(1,0,1);
    return res;
}

int main(){

    n=read();
    printf("%lld\n",part(n));

    
    return 0;
}

D 合法序列

注意到該題的k很小,最大也只能到4,所以我們可以列舉這k位,而這k位對整個序列合法性判斷上的影響也只有16位,也可以直接列舉,最大列舉量為\(2^{2^4}\),即131072

我們每列舉到一個\(2^{2^k}\)的狀態,就判斷它是不是合法的,如果它不合法,那麼就捨去這種情況,如果合法,就繼續轉移

轉移方程方面,我們設f[i][S]為從第i位開始k位是狀態S,我們在枚舉出來的\(2^{2^k}\)的狀態必然合法,那麼我們把最後一個長度為k的串在f陣列中標記為1

接下來就把這個列舉的序列不斷向後移去轉移

轉移有點難

我們需要先把列舉的狀態轉移成上一個狀態,即取該狀態的後k-1位,然後左移一位,就是上一個狀態,最後一位取1時函式值與最後一位取0時函式值之和就是該狀態的函式值

用到了a或上b再異或上b就是把b是1的位在a中都設成0

碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
using namespace std;
const int INF=0x3f3f3f3f,N=510,Mod=998244353;

int n,k,res=0;
int g[N],f[N][N];

inline int read(){
    int x=0,y=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*y;
}

int main(){

	n=read(),k=read();

	for(int i=0;i<(1<<k);i++){
		for(int j=0;j<k;j++){
            g[i]|=((i>>j)&1)*(1<<(k-j-1));
        }
	}//編號和狀態位置相反

	for(int i=0;i<(1<<(1<<k));i++){
		int flg=1;
		for(int j=0;j<=(1<<k)-k;j++){
			int op=((1<<k)-1)&(i>>j);//取該狀態的每個k位
			op=g[op];
			if(i&(1<<op)) continue;//合法嗎
			flg=0;
			break;
		}

		if(!flg)continue;
		memset(f,0,sizeof(f));

		f[(1<<k)-1][i>>((1<<k)-k)]=1;

		for(int j=(1<<k);j<n;j++){//從16開始往後dp
            for(int l=0;l<(1<<k);l++){//列舉狀態
                if((i&(1<<g[l]))==0) continue;
                
                int op=((l|(1<<(k-1)))^(1<<(k-1)))<<1;
			//cout<<j<<" "<<l<<" "<<op<<endl;

                f[j][l]=(f[j-1][op|1]+f[j-1][op])%Mod;
            }
		}

		for(int j=0;j<(1<<k);j++) res=(res+f[n-1][j])%Mod;

	}
	
    printf("%d\n",res);
    
    
    return 0;
}