ACM训练日记—4月26日(省赛练习1)

本文详细解析了HDU在线评测系统中的几道经典题目,包括动态规划、矩阵快速幂、完美匹配等问题的解决思路及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       今天折腾的真是够呛,明天运动会就不去凑热闹了,老老实实A题看博客,下午集中精力打比赛了。

HDU—6024

题意:

     在一条线上有很多教室,可以在教室建立糖果店,每个教室都有坐标和建立糖果店的费用。如果该教室没有建立糖果店,那么在这个教室左边的糖果店到此教室的距离为此教室的费用,问最小的修建糖果店的费用是多少。

    注意在第一个点上一定要建立糖果店。

来自:https://blog.youkuaiyun.com/a1046765624/article/details/79878557

dp[i][j]为到第i个教室,前一个糖果店的位置是j,的最小费用是多少。
分两种,一是在这里修建糖果店。dp[i][i]=min(dp[i][i],dp[i-1][j]+v[i])(1<=j<i)。
二是不修建糖果店。dp[i][j]=min(dp[i][j],dp[i-1][j]+d[i]-d[j])(1<=j<i)。

还有一种思路:

来自:https://blog.youkuaiyun.com/STILLxjy/article/details/72514980

      根据题目所给的时间,和题目的数据的大小,我们可以知道题目可以承受住时间复杂度为O(n^2)的算法。
并且每个教室只有两种方案,要么建超市,要么不建。这就很像是背包问题了,所以我们就想到了dp.
我们设dp[i][0]表示在教室i不建超市时前i个教室的费用和的最小值;dp[i][1]表示在教室i建超市时前i个教室的费用和的最小值


        那么我们很快可以得出: dp[i][1] = min(dp[i-1][0],dp[i-1][1]) + ci
关于dp[i][0],由于可以承受住时间复杂度为O(n^2)的算法,那么我们就可以想到枚举离教室i最近的超市j的位置,然后取所有情况的最小值就可以了。
假设左边最近超市为j,那么教室j+1~教室i都不能建超市,所以教室j+1~教室i的费用分别为他们的位置到教室j之间的距离了。当前dp[i][0] = dp[j][1] +( 教室j+1~教室i的费用)
如果我们暴力求解,那么时间复杂度会变成O(n^3),会超时。但是我们会发现由于j是从大到小变化的,所以就可以用:t += (i - j) * (nodes[j+1].x - nodes[j].x);来记录教室j+1~教室i的费用和了。
关于 t += (i - j) * (nodes[j+1].x - nodes[j].x); 的解释: 
比如我们要算x3 - x1 , x2 - x1的sum,那么由于保证了x是升序排列的,所以sum = (x3 - x2) + 2 * (x2 - x1).

const int maxn=3200;//第一种思路
long long dp[maxn][maxn];
struct poin
{
    int x,v;
}a[maxn];
int cmp(poin q,poin w)
{
    return q.x<w.x;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i].x,&a[i].v);
        }
        sort(a+1,a+1+n,cmp);
        memset(dp,0x3f3f3f3f,sizeof(dp));
        dp[1][1]=a[1].v;
        for(int i=2;i<=n;i++)
        {
            dp[i][i]=dp[i][i-1]+a[i].v;
            for(int j=1;j<i;j++)
            {
                dp[i][j]=min(dp[i][j],dp[i-1][j]+a[i].x-a[j].x);
                dp[i][i]=min(dp[i][i],dp[i-1][j]+a[i].v);
            }
        }
        long long sum=0x3f3f3f3f;
        for(int i=1;i<=n;i++)
        {
            sum=min(sum,dp[n][i]);
        }
        printf("%lld\n",sum);
    }

    return 0;
}

HDU—6025

题意:

       给出n个数,问去掉其中一个数ai,问gcd(a1,a2...ai-1,ai+1...an)最大是多少

 找两个数组,处理前i个gcd的值和后i个数的gcd,然后暴力每一个i,maxn=max(maxn,gcd(a[i-1],b[i+1]));就可以了

ll gcd(ll x,ll y)
{
    if(y==0) return x;
    else return gcd(y,x%y);
}
ll a[100005],b[100005];
ll c[100005];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ll n;
        ll t;
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&c[i]);
        }
        t=c[1];
        for(int i=1;i<=n;i++)
        {
            t=gcd(t,c[i]);
            a[i]=t;
        }
        t=c[n];
        for(int i=n;i>=1;i--)
        {
            t=gcd(t,c[i]);
            b[i]=t;
        }
        ll maxn=max(b[2],a[n-1]);
        for(int i=2;i<=n-1;i++)
        {
            maxn=max(maxn,gcd(a[i-1],b[i+1]));
        }
        cout<<maxn<<endl;
    }
}

HDU—6027 水题,不说了

HDU—6030

题意: 给出红蓝两种,然后排成一个字符串,要求在每一个长度为素数的区间里面是的r(red)的数量不小与b(blue)的数量;

     其实写一下前几项就能发现规律f[i]=f[i-1]+f[i-3];f[2]=3;f[3]=4;f[4]=6;f[5]=9;f[6]=13;

然后构造矩阵,矩阵快速幂就可以了。

const LL mod=1e9+7;
struct Matrix
{
    LL m[5][5];
};
LL n, c;
int ssize = 4;
Matrix Mul(Matrix a,Matrix b)
{
    int i, j, k;
    Matrix c;
    for(i = 1; i <= ssize; i++)
    {
        for(j = 1; j <= ssize; j++)
        {
            c.m[i][j]=0;
            for(k = 1; k <=ssize;k++)
            {
                c.m[i][j]+=(a.m[i][k]*b.m[k][j])%mod;
                c.m[i][j]%=mod;
            }
        }
    }
    return c;
}


Matrix pow_mod(Matrix m,LL n)
{
    Matrix b;
    memset(b.m,0,sizeof(b.m));
    for(int i=1;i<=4;i++)
    {
        b.m[i][i]=1;
    }
    while(n>0)
    {
        if(n&1) b=Mul(b,m);
        n=n>>1;
        m=Mul(m,m);
    }
    return b;
}
int main()
{
    LL T,n,k;
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld",&n);
        if(n==2) {cout<<3<<endl;continue;}
        if(n==3) {cout<<4<<endl;continue;}
        if(n==4) {cout<<6<<endl;continue;}
        else if(n==5) {cout<<9<<endl;continue;}
        Matrix str;
        str.m[1][1]=str.m[1][3]=str.m[3][2]=1;//构造矩阵快速幂
        str.m[2][2]=str.m[2][4]=str.m[4][3]=1;
        str.m[1][2]=str.m[1][4]=str.m[2][1]=0;
        str.m[2][3]=str.m[3][1]=str.m[3][4]=0;
        str.m[3][3]=str.m[4][4]=str.m[4][1]=0;
        str.m[4][2]=0;
        n-=5;
        str=pow_mod(str,n);
        LL ans=0;
        ans=(str.m[1][1]*9%mod+str.m[1][2]*6%mod+str.m[1][3]*4%mod+str.m[1][4]*3%mod)%mod;
        cout<<ans<<endl;
    }
    return 0;
}

HDU—6029

题目描述:一种所有结点都有边与之相连的匹配叫做完美匹配。现在你有N个结点,对于n-1个结点,你有两种操作:
(1)将这个结点与之前的所有结点都连一条边
(2)不进行操作;

       来自:https://blog.youkuaiyun.com/weixin_39453270/article/details/80026538

 题面分析:这是一道很有意思的思维题。首先要明确的一点是题目只要我们求是否可能是完美匹配,而不是让我们判断是否一定是完美匹配。对于每个结点,当我们要进行第一种操作时,即意味着我们这个结点可以与前面的任意一个结点进行匹配;而当我们进行第二种操作的时候,意味着这个结点是完全孤立的。因为每次操作1时,当前节点的匹配是任意的,考虑到这点,我们可以考虑使用队列去做,即当进行操作1的时候,队列深度减1,当操作为2时,将队列深度加1,最后判断队列是否非空即可。

int a[MAXN];
int n;
int main()
{


    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=2;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        if(n%2==1)printf("No\n");
        else
        {
        a[1]=2;
        int f=0;
        int sum=0;
        for(int i=n;i>=1;i--)
        {
            if(a[i]==1)sum++;
            else if(a[i]==2)sum--;
            if(sum<0){f=1;break;}
        }
        if(f)printf("No\n");
        else printf("Yes\n");
        }
    }
    return 0;
}

HDU—6020

题意:

官方题解:

这题更多算是想法题,有脑洞的同学可以各种乱搞。将问题转化成:求去掉KK位数字后,不含前导零,且数字和是否能被三整除。按照预测,这里应该会汇聚本场最多的hack点。

我们设S0S0S1S1S2S2分别为原串上mod 3=0mod3=01122数字的个数。 我们假定删除取模后为001122的数字各AABBCC个,则显然有0<=A<=S0,0<=B<=S1,0<=C<=S20<=A<=S0,0<=B<=S1,0<=C<=S2K=A+B+CK=A+B+CSumSum mod 3=(A*0+B*1+C*2)mod 3=(S0*0+S1*1+S2*2)mod 3=biasmod3=(A0+B1+C2)mod3=(S00+S11+S22)mod3=bias。 枚举CC的值,我们可得B mod 3=(bias-C*2)mod 3,A=K-B-CBmod3=(biasC2)mod3,A=KBC。如果有若干组A,BA,B不逾界,可知这些(A,B,C)(A,B,C)是在模意义下合法的解,但不一定满足没有前导零。

所以,对于【大于00的数】我们贪心地从后往前删除,对于00我们贪心地从前往后删除。

需要统计出:a3a3=第一个【mod 3=0mod3=0且非00的数】前00的个数(如果mod 3=0mod3=0且非00的数不存在,那么a3a3就取所有零的个数),E1E1=【第一个00前是否存在mod 3=1mod3=1的数】,E2E2=【第一个00前是否存在mod 3=2mod3=2的数】。

则以下情况满足任一种都能保证无前导零:A>=a3A>=a3B<B<S1S1E1E1C<C<S2S2E2E2


还需要加一种情况 满足k==n-1 也是yes

      

      这道题其实我的思路和它差不多,就是在n个里面选n-k个加和为3的倍数的情况,先预处理后i个0,1,2,3出现的次数,然后暴力起点(s[i]!=0),接下来还是暴力所有情况。

我感觉可能会超时,明天还得写写看看

来自:https://blog.youkuaiyun.com/luricheng/article/details/69264096

代码:https://blog.youkuaiyun.com/luricheng/article/details/69264096

const int inf = 1e9 + 7;


const int N = 100000 + 5;
char ch[N];
int x[N];


bool solve(int n,int k)
{
    for(int i=0;i<n;++i)
    {
        x[i]=ch[i]-'0';
        x[i]%=3;
    }
    int s[3]={0,0,0};
    int sum=0;
    for(int i=0;i<n;++i)
    {
        s[x[i]]++;
        sum+=x[i];
    }
    int a3=0;//>0&&mod3==0的第一数 前面的0的个数
    bool e1,e2=e1=0;
    for(int i=0;i<n;++i)
    {
        if(ch[i]!='0'&&x[i]==0) break;
        if(x[i]==0) a3++;
        if(x[i]==1) e1=1;
        if(x[i]==2) e2=1;
    }
    for(int c=0;c<=s[2];++c)
    {
        int minb=((sum-2*c)%3+3)%3;
        for(int b=minb;b<=s[1];b+=3)
        {
            int a=k-b-c;
            if(a>=0&&a<=s[0])
            {
                if((a>=a3)||(e1&&b<s[1])||(e2&&c<s[2])) return 1;
                if(k==n-1) return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,k;
        scanf("%d%d%s",&n,&k,ch);
        if(solve(n,k)) cout<<"yes"<<endl;
        else cout<<"no"<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值