zoukankan      html  css  js  c++  java
  • 20171018校内训练

    方法1:

    考虑贪心,尽量把所有刀数全部割在一边是最优的,如果不行,把一边割成全都是1,剩下的刀数都割在另一边。

    方法2:

    我们有另外一个可以保证正确性的方法。

    我们定义这几个量:i:在行上割的刀数,j:在列上割的刀数,len:行上每一段的距离

    显然,我们把i刀尽量平分在行上,j刀尽量平分在列上最优。i+j=k。

    所以,我们只要知道i,len中的其中任意一个值,就可以求出其它两个值,从而计算出答案。

    我们发现,i*len<=n

    ∴i<=√n,len<=√n

    这样,我们只要分别把i从0枚举到√n,把len从1枚举到√n,即可计算出答案。

    注意,由于我们是枚举i,所以i一定会小于n,但i不能大于k,此时计算出的j一定小于k,但j要<=m-1,否则在列上根本割不出j刀

    又由于我们是枚举len,所以i有可能大于k,但是此时的i不能舍去,而要看做在列上一刀也不割,因为此时如果len++后i就有可能小于k,那么在列上还要割几刀,不一定有在行上割超过k刀(其实可以看做就割上面的k刀,剩下的刀不割),列上不割更优。但j要<=m-1,否则在列上根本割不出j刀

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int main()
    {
    //    freopen("cut.in","r",stdin);freopen("cut.out","w",stdout);
        long long n,m,k,ans=-1ll;cin>>n>>m>>k;
        if(k>n+m-2)return 0*puts("-1");
        for(long long i=0;i*i<=n&&i<=k;i++)//横着割的刀数,不能超过k 
        {
            long long j=k-i;
            if(j>=m)continue; 
            ans=max(ans,(n/(i+1))*(m/(j+1)));
        } 
        for(long long len=1;len*len<=n;len++)//当每段长为len时,横着最多可以割几刀 
        {
            long long i=n/len-1;long long j=max(0ll,k-i);
            if(j>=m)continue;
            ans=max(ans,(n/(i+1))*(m/(j+1)));
        }
        cout<<ans;
        return 0;
     } 
    View Code

     

    状压DP。

    首先,我们可以发现a[i]<=30,这样你取的数的值不会大于58(因为大于58的数都可以用1代替)

    <=58的质数一共有16个。这时,我们想到了什么?状压DP!

    用f[i][S]表示前i个数(S的二进制第i位上为1表示选第i+1个质数(这些质数必须分别是前面选的i个数的因数))

    然后我们就需要一些奇技淫巧。

    首先预处理一个数组shu[i]表示数(i+2)的质因子,然后把它转成二进制存起来(二进制第i位上为1表示第i+1个质数是该数的因数)

    为什么这么存呢?因为一个数可以分解成几个质数的乘积,即使一个质数的指数>1,只要其它数中没有出现过该质数,那么它们也一定互质,所以只要记每个质数是否出现过即可。

    举个例子:50=2*5*5,则它的因数里有2和5这两个质数,而2是第1个质数,5是第3个质数,则shu[50]=1*2^0+0*2^1+1*2^2=5=(101)(二进制下)

    求出这个有什么用呢?我们知道,若m个数两两互质,则它们的shu[i] & 起来一定为0(不算1的shu),否则就有相同的因数

    这样,我们的状态转移方程就出来了:

    f[i][j]=f[i-1][j]+a[i]-1(第i位取1)

    f[i][j]=max(f[i-1][j^(异或)shu[k-2](shu[i]表示数(i+2))]+abs(a[i]-k)) (2<=k<=58&&shu[k-2]&j==0)

    j&shu[k-2]的意思是k的因数中的质数必须在当前状态选择的质数中出现过(否则根本就不能选k)

    j^shu[k-2]的意思是k的因数中的质数在上一个状态选择的质数中必须都没有出现过(否则就不互质)

    最后在f[n][0~2^16-1]中取一个最大值(因为f[i][S]的定义是这些质数一定都要出现过,而实际上有的时候不全部选更优)

    等等,还没说怎么输出方案呢?

    我们用pre[i][S]表示f[i][S]这个状态是由f[i-1][pre[i][S]]转移来的,sum[i][S]表示在f[i][S]最优的情况下,第i位选的数。

    这些都是可以在转移时记录下来的。

    输出答案时,只要倒序输出sum[i][pre[i][S]]即可(从后往前遍历i,S为最优答案时的选的质数,然后让S=pre[i][S])

    求shu[i]:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int pri[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
    int main()
    {
        for(int i=2;i<=58;i++)
        {
            int S=0,k=i,x=1;
            for(int j=0;j<16;j++)
            {
                if(k%pri[j]==0)S=S+x;
                while(k%pri[j]==0)
                {
                    k=k/pri[j];
                }
                x=x*2;
            }
            cout<<S<<",";
        }
    }
    View Code

    状压DP:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int pre[101][70000],sum[101][70000];
    int f[110][70000],a[110];
    int shu[57]={1,2,1,4,3,8,1,2,5,16,3,32,9,6,1,64,3,128,5,10,17,256,3,4,33,2,9,512,7,1024,1,18,65,12,3,2048,129,34,5,4096,11,8192,17,6,257,16384,3,8,5,66,33,32768,3,20,9,130,513};
    int ans[110],Min=999999999;
    int Abs(int x){return x>0?x:-x;}
    int main()
    {
    //    freopen("array.in","r",stdin);freopen("array.out","w",stdout);
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        for(int j=0;j<=(1<<16)-1;j++)
        {
            sum[i][j]=1;pre[i][j]=j;f[i][j]=f[i-1][j]+a[i]-1;
            for(int k=2;k<=58;k++)
            {
                if(((j&shu[k-2])==shu[k-2])&&(f[i-1][j^shu[k-2]]+Abs(k-a[i])<f[i][j]))
                {
                    f[i][j]=f[i-1][j^shu[k-2]]+Abs(k-a[i]);sum[i][j]=k;pre[i][j]=j^shu[k-2];
                }
            }
        }
        for(int j=0;j<=(1<<16)-1;j++)
            if(f[n][j]<Min)
            {
                int k=j;Min=f[n][j];
                for(int i=n;i>=1;i--){ans[i]=sum[i][k];k=pre[i][k];}
            }
        for(int i=1;i<=n;i++)printf("%d ",ans[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    Java多线程系列 基础篇10 wait/notify/sleep/yield/join
    Java多线程系列 基础篇09 Object.wait/notifyJVM源码实现
    Java多线程系列 基础篇07 synchronized底层优化
    Java多线程系列 基础篇06 synchronized(同步锁)
    Java多线程系列 基础篇05 synchronized关键字
    Java多线程系列 基础篇04 线程中断
    Java多线程系列 基础篇03 线程的优先级和守护线程
    Java多线程系列 基础篇02 线程的创建和运行
    MySQL进阶14--标识列(自增序列/auto_increment)--设置/展示步长--设置/删除标示列
    MySQL进阶13--常见六大约束: 非空/默认/主键/唯一约束/检查约束/外键约束--表级约束 / 列级约束
  • 原文地址:https://www.cnblogs.com/lher/p/7695998.html
Copyright © 2011-2022 走看看