NOIP模拟题[贪心][DP][数论]

时间:2021-11-30 18:44:40

改程序之前,写程序之前,确保自己理解了,不然效率会很低。
写程序少用复制黏贴,容易细节出错,不好调试。
T1:
题意:
给定两字符串,判断B串是否是A串的字串且输出B串每个字母的匹配位置字典序最大的匹配方案。
分析:
典型贪心,特别是“字典序最大”,不过好久没写贪心了有点迟钝233.
从后向前遍历B串和A串,找到B串单词的第一个匹配位置即可比较下一个。

#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 modd 1e9+7
#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) for(int i=1;i<=n;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 "door"
//for(int i=1;i<=n;i++)
//(double) (ll) LL (int)
//(double)clock()/CLOCKS_PER_SEC
using namespace std;
const int Maxn=1e6+5;
int lena,lenb,cur,ans[Maxn];
char a[Maxn],b[Maxn],tmp;
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()
{
    scanf("%s",a);lena=strlen(a);
    if(!lena){printf("No");return;}
    scanf("%s",b);lenb=strlen(b);
    cur=lena-1;
}
void work()
{
    if(lenb>lena){
        printf("No");return;
    }
    for(int i=lenb-1;i>=0;i--){
        for(cur;cur>=0;cur--)
            if(a[cur]==b[i]){
                ans[i]=cur+1;cur--;break;
            }
    }
    if(ans[0]){
        printf("Yes\n");
        for(int i=0;i<lenb;i++)
            printf("%d\n",ans[i]);
    }
    else printf("No");
}
void debug()
{
    //
}
int main()
{
    freopen(PROC".in","r",stdin);
    freopen(PROC".out","w",stdout);
    init();
    work();
    //debug();
    return 0;
}

T2:
题意:
给定一地图,偶数行相连的0看做一权值为0的个数的点,求从第一行走到第n行的最大权值和。
分析:
显然可以DP,数据范围简直是为DP设计的,f[i][j]表示i行j列及以下的最大权值和,判断能不能走到就对无法到达下一层且不是最后一层的位置f值直接置0即可。
图论做法:缩点,拓扑排序避免多次修改以后直接走SPFA;适用宽度更大的情况。
拓展:
读错题了以为只取经过的位置的权值,那么就要加一维表示来的方向,再在同一行从左向右,从右向左各扫一遍记录从左来或从右来的值。

#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 modd 1e9+7
#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) for(int i=1;i<=n;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 "ant"
//for(int i=1;i<=n;i++)
//(double) (ll) LL (int)
//(double)clock()/CLOCKS_PER_SEC
using namespace std;
const int Maxn=1e3+5;
int n,m,ans,idx;
int a[5*Maxn][2*Maxn],f[5*Maxn][2*Maxn];
int maxn[Maxn*2],num[Maxn*2];
char tmp;
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()
{
    n=read();m=read();
    if((!n)||(!m))return;
    each(i,n){
        while(tmp>'1'||tmp<'0')tmp=getchar();
        each(j,m){
            a[i][j]=tmp-'0';
            tmp=getchar();
        }
    }
}
void work()
{
    for(int i=n;i>0;i--){
        int lll=!(i%2);
        num[0]=maxn[0]=0;
        idx=0;
        for(int j=1;j<=m;j++)
            if(!a[i][j]){
                maxn[idx]=max(maxn[idx],f[i+1][j]);
                num[idx]+=lll;
            }
            else ++idx,maxn[idx]=num[idx]=0;
        idx=0;
        for(int j=1;j<=m;j++)
            if(!a[i][j]){
                if(maxn[idx]||i==n)
                f[i][j]=maxn[idx]+num[idx];
            }
            else ++idx;
    }
    each(j,m)ans=max(ans,f[1][j]);
    if(ans)printf("%d",ans);
    else printf("-1");
}
void debug()
{
    //
}
int main()
{
    freopen(PROC".in","r",stdin);
    freopen(PROC".out","w",stdout);
    init();
    work();
    //debug();
    return 0;
}

T3:
题意:
在矩阵里求长度为wi的线段的条数(可连点);
分析:
显然知道若该线段“长”为x,“宽”为y,则条数为(n-x)*(m-y)*2;那么重点是求x^2+y^2=wi^2的整数解。
考虑剪枝,但若用暴力不好剪枝,继续分析发现,可以用gcd来将方程变为求完全平方数(如下:
x^2+y^2=wi^2;
y=sqrt((wi-x)(wi+x));
那么此时提出两式的gcd,根号内内容互质又能开出正整数,则只能是两个完全平方数,但这里直接求满足完全平方数的两式还是不方便,考虑变形:
令 a*a=wi-x,/gcdb*b=wi+x/gcd;
则a*a+b*b=2*wi/gcd;
至此就缩小了数据范围。
不过引入了一个变量,所以我们要枚举两个变量,由于右式是除法,则只用枚举sqrt(2*wi),再在下面讨论两种情况即可。

#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 modd 1e9+7
#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) for(int i=1;i<=n;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 "amount"
//for(int i=1;i<=n;i++)
//(double) (ll) LL (int)
//(double)clock()/CLOCKS_PER_SEC
using namespace std;
int t;
long long n,m,w,ans,a,b,ww;
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()
{
    n=read();m=read();t=read();
}
ll _gcd(ll xxx,ll yyy)
{
    return yyy==0?xxx:_gcd(yyy,xxx%yyy);
}
void work()
{
    w=read();ans=0;
    if(w<n)ans+=(n-w)*m;
    if(w<m)ans+=(m-w)*n;
    ww=w*w;w<<=1;
    for(ll i=1;i*i<=w;i++)
    if(!(w%i)){
        for(ll j=1;j*j<=w/i/2LL;j++){
            long long k=floor((double)sqrt((double)w/(double)i-(double)j*(double)j));
            //if(j>=k)continue;
            if(k*k!=w/i-j*j)continue;
            if(k==j||_gcd(k,j)!=1ll)continue;
            //if(k<=0||j<=0)continue;
            a=k*k*i-j*j*i;a/=2ll;
            b=i*k*j;
            //if(a*a+b*b!=ww)continue;
            //if(i!=_gcd(w/2-a,w/2+a))continue;
            if(a<n&&b<m)ans+=(n-a)*(m-b);
            if(b<n&&a<m)ans+=(m-a)*(n-b);
        }
        if(i*i!=w)
        for(int j=1;j*j<=i/2LL;j++){
            long long k=floor((double)sqrt((double)i-(double)j*(double)j));
        // if(j>=k)continue;
            if(k*k!=i-j*j)continue;
            if(k==j||_gcd(k,j)!=1ll)continue;
            //if(k<=0||j<=0)continue;
            a=k*k*w/i-j*j*w/i;a/=2ll;
            b=k*j*w/i;
            //if(a*a+b*b!=ww)continue;
            //if(w/i!=_gcd(w/2-a,w/2+a))continue;
            if(a<n&&b<m)ans+=(n-a)*(m-b);
            if(b<n&&a<m)ans+=(m-a)*(n-b);
        }
    }
    printf("%I64d ",ans);
}
void debug()
{
    //
}
int main()
{
    freopen(PROC".in","r",stdin);
    freopen(PROC".out","w",stdout);
    init();
    each(i,t)
    work();
    //debug();
    return 0;
}