7.5 模拟赛

7.5 模拟赛

DP专题

A 涂色paint

明显的区间DP

转移方程:

[f[i][j]=egin{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
",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
",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
",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
",res);
    
    
    return 0;
}

原文地址:https://www.cnblogs.com/wsyunine/p/14974050.html