NOIP模拟题 [递推][DP][搜索]

时间:2022-09-15 18:45:46

T1:
题意:
要求将1-n的数排成两列,使得两列数的个数相等且都递增,还要求其中一列比另一列对应位置上的数大。
分析:
两个方法:
1.先写个暴力打表找规律,因为反正也要写对拍。
2.分析一下:
如果我们把1-n看做一个n位二进制数,用0表示在第一排,1表示在第二排,则前面的0数必须大于等于1数,所以我们就会想到求n/2个数的进出栈顺序,而后者是卡特兰数的基本运用。
对于进出栈能用卡特兰数求的证明如下:
n/2个数中,设第i个数出栈后栈为空,则根据乘法原理,方案总数为:CI*CN/2-I(i [1,N/2])
然后位置对应不上,所以虽然递推关系是一样的,初始值还是要自己赋。
还涉及到除法取mod,求逆元,我用的费马小定理,还可以exgcd,线性递推。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<vector>
#include<ctime>
#define ll long long 
#define inf 2e8
#define clr(x) memset(x,0,sizeof(x))
#define maxen(x) memset(x,127,sizeof(x))
#define maxer(x) memset(x,31,sizeof(x))
#define minus(x) memset(x,-1,sizeof(x))
#define each(i,n,m) for(int i=n;i<m;i++)
#define eachrev(i,n,m) for(int i=n;i>m;i--)
#define minn(a,b,c) min(a,min(b,c))
#define maxx(a,b,c) max(a,max(b,c))
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define PROC "A"
//for(int i=1;i<=n;i++)
//(double) (ll) LL (int)
//(double)clock()/CLOCKS_PER_SEC
using namespace std;
const int Maxn=2e6+5;
const long long modd=1e9+9;
long long c,ans[Maxn];
int t,n;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void init()
{
    t=read();
    ans[0]=1;
    for(int i=1;i<=2e6;i++)
        ans[i]=ans[i-1]*i%modd;
}
ll quick(int pos)
{
    int b=1e9+7;
    ll ret=1;
    ll ttt=ans[pos];
    while(b){
        if(b&1)(ret*=ttt)%=modd;
        (ttt*=ttt)%=modd;
        b>>=1;
    }
    return ret;
}
ll calc()
{
    return ans[n]*quick(n/2)%modd*quick(n/2+1)%modd;
}
void work()
{
    for(int i=1;i<=t;i++){
        n=read();
        printf(lld"\n",calc());
    }
}
void debug()
{
    //
}
int main()
{
    freopen(PROC".in","r",stdin);
    freopen(PROC".out","w",stdout);
    init();
    work();
    //debug();
    return 0;
}

T2:
题意:
对于一个给定序列,给定初始值,求要使得在加上该序列数的过程中一直都大于等于零最少要删去多少个数。
分析:
我开始想的贪心,因为可能T就优化到了nm,后来发现是错的,我的表示没有考虑后效性,要吃一堑长一智啊。(其实直接贪心可以强行卡过);
DP:F[I][J]表示,删去j个数,i-n这一段会出现的前缀和的最小值(因为能不能过其实取决于最小值)。
然后我们考虑逆推,为什么呢,因为逆推的话,在前面选择一个数,后面的前缀和其实也会加上这个数,也就是说,这保证了我们能够以:
F[I][J]=MIN(0,F[I+1][J])+A[I]的方式求F。(即忽略前面的影响,而如果顺推则不可忽略)。
如果顺推,要用一个二维数组表示当前情况的前缀和。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<vector>
#include<ctime>
#define ll long long 
#define inf 2e8
#define clr(x) memset(x,0,sizeof(x))
#define maxen(x) memset(x,127,sizeof(x))
#define maxer(x) memset(x,31,sizeof(x))
#define minus(x) memset(x,-1,sizeof(x))
#define each(i,n,m) for(int i=n;i<m;i++)
#define eachrev(i,n,m) for(int i=n;i>m;i--)
#define minn(a,b,c) min(a,min(b,c))
#define maxx(a,b,c) max(a,max(b,c))
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define PROC "B"
//for(int i=1;i<=n;i++)
//(double) (ll) LL (int)
//(double)clock()/CLOCKS_PER_SEC
using namespace std;
const int Maxn=1e3+5;
const long long modd=1e9+7;
long long f[Maxn];
long long n,m,minu,a[Maxn],cnt,tmp,q,res[Maxn][Maxn];
long long read()
{
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
void init()
{
    n=read();m=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        if(a[i]<0)minu++;
    }

    for(int i=n;i>=1;i--){
        res[i][0]=min(0LL,res[i+1][0])+a[i];
        for(int j=1;j<=n-i;j++){
            res[i][j]=min(0LL,res[i+1][j])+a[i];
            res[i][j]=max(res[i][j],res[i+1][j-1]);
        }
    }
}
void modi()
{
    int lef=0,rig=minu+1;
        while(lef<rig){
            int mid=(lef+rig)>>1;
            if(q+res[1][mid]<0)lef=mid+1;
            else rig=mid;
        }
    printf("%d\n",lef);
}
void work()
{
    for(int i=1;i<=m;i++){
        q=read();
        modi();
    }
}
void debug()
{
    //
}
int main()
{
    freopen(PROC".in","r",stdin);
    freopen(PROC".out","w",stdout);
    init();
    work();
    //debug();
    return 0;
}

T3:
题意:
一个有权值的数独,分数为填在该位的数乘以该位权值,求最大得分。
分析:
搜索T飞,考虑到可能会因为顺序不同出现大量重复情况,就排个搜索顺序(因为顺序不对结果产生影响),然后还有就是从限制多的地方开始搜,这样可以避免前面搜了很久最后到了最后一个被打回去的情况,也就是,把一棵胖的树倒过来就瘦了。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<vector>
#include<ctime>
#define ll long long 
#define inf 2e8
#define clr(x) memset(x,0,sizeof(x))
#define maxen(x) memset(x,127,sizeof(x))
#define maxer(x) memset(x,31,sizeof(x))
#define minus(x) memset(x,-1,sizeof(x))
#define each(i,n,m) for(int i=n;i<m;i++)
#define eachrev(i,n,m) for(int i=n;i>m;i--)
#define minn(a,b,c) min(a,min(b,c))
#define maxx(a,b,c) max(a,max(b,c))
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define PROC "C"
//for(int i=1;i<=n;i++)
//(double) (ll) LL (int)
//(double)clock()/CLOCKS_PER_SEC
using namespace std;
const int fz[10][10]=
{{6,6,6,6,6,6,6,6,6},
{6,7,7,7,7,7,7,7,6},
{6,7,8,8,8,8,8,7,6},
{6,7,8,9,9,9,8,7,6},
{6,7,8,9,10,9,8,7,6},
{6,7,8,9,9,9,8,7,6},
{6,7,8,8,8,8,8,7,6},
{6,7,7,7,7,7,7,7,6},
{6,6,6,6,6,6,6,6,6}};
const int Maxn=15;
int mapy[Maxn][Maxn],lim[Maxn][Maxn],orde[Maxn*Maxn][2],bloc[Maxn][Maxn];
int lin[Maxn][Maxn],lie[Maxn][Maxn],used[Maxn][Maxn];
int m,maxn,ans1,ans2;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int getblock(int x,int y)
{
    x--;y--;
    return x/3*3+y/3+1;
}
void mark(int x,int y)
{
    int cur=getblock(x,y);
        used[x][y]=1;
    for(int i=1;i<=9;i++)
        lim[x][i]++;
    for(int i=1;i<=9;i++)
        lim[i][y]++;
    int tmpx=(x-1)/3*3,tmpy=(y-1)/3*3;
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            lim[tmpx+i][tmpy+j]++;
}
void init()
{
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++){
            mapy[i][j]=read();
            if(mapy[i][j]){ 
                int cur=getblock(i,j);
                lin[i][mapy[i][j]]=1;
                lie[j][mapy[i][j]]=1;
                bloc[cur][mapy[i][j]]=1;
                mark(i,j);
                m++;
                ans1+=fz[i-1][j-1]*mapy[i][j];
            }
        }
    for(int i=1;i<=81-m;i++){
            int maxnnum=-1;
            int &x=orde[i][0];
            int &y=orde[i][1];
            for(int k=1;k<=9;k++)
                for(int z=1;z<=9;z++)
                    if((!used[k][z])&&lim[k][z]>maxnnum){
                        maxnnum=lim[k][z];
                        x=k;
                        y=z;
                    }
            mark(x,y);
        }
}
void dfs(int cur)
{
    if(cur==81-m+1){
        maxn=max(maxn,ans2);return;
    }
    int tovx=orde[cur][0];
    int tovy=orde[cur][1];
    int blockk=getblock(tovx,tovy);
    for(int i=9;i>=1;i--){
        if(lin[tovx][i]||lie[tovy][i]||bloc[blockk][i])continue;
        ans2+=i*fz[tovx-1][tovy-1];
        lin[tovx][i]=1;
        lie[tovy][i]=1;
        bloc[blockk][i]=1;
        dfs(cur+1);
        lin[tovx][i]=0;
        lie[tovy][i]=0;
        bloc[blockk][i]=0;
        ans2-=i*fz[tovx-1][tovy-1];
    }
}
void work()
{
    dfs(1);
    if(maxn||m==81)printf("%d",ans1+maxn);
    else printf("-1");
}
void debug()
{
    //
}
int main()
{
    freopen(PROC".in","r",stdin);
    freopen(PROC".out","w",stdout);
    init();
    work();
    //debug();
    return 0;
}