各种优化2018-08-13

第一道题:https://vjudge.net/contest/246647#problem/A
这道题相信很多人一开始都想暴力分组,将每个石头都按照01分组,0就是一堆,1就是另一堆,然后在做差比较。

这道题的操作并不是这样的!!!!!

正确操作:
这道题就是个变向的背包问题
如何背包呢???

既然是要使分组之差最小,那么每一堆的差一定不能小于石子之和除以2!!!

所以,这不就是背包的总容量吗???!!!
那么,我们就可以根据所有石头的和/2来当背包的最大容量!!!
正确AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=25;
int a[maxn],f[1000000],sum;
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum+=a[i];
    }
    int m=sum/2;
    for(int i=1;i<=n;i++)
        for(int j=m;j>=a[i];j--)
            f[j]=max(f[j],f[j-a[i]]+a[i]);
    cout<<sum-2*f[m];
    return 0;
}

第二道题:https://vjudge.net/contest/246647#problem/B
这道题我一开始就想到了暴力算法,直接暴力枚举所有的和为Ai的素数,在从中选择最小值即可,竟然也AC了!
暴力算法:

#include <iostream>
#include <math.h>
using namespace std;
const int maxn=100000+10;
int pd(int n){
    if(n==2)
       return 1;
    for(int i=2;i<=sqrt(n);i++)
        if(n%i==0)
           return 0;
    return 1;
}   
int main(){
    while(1){
        int n;
        cin>>n;
        if(n==0)
            return 0;
        for(int i=2;i<=n/2;i++){
            if(pd(i)==1&&pd(n-i)==1){
                cout<<n<<" ="<<i<<" + "<<n-i<<endl;
                break;
            }
        }
    }
    return 0;
}

要继续优化,就要用到素数筛法了!!!
素数筛法,就是在原代码的基础上,将算法变成线性的。
那么,怎么操作呢??!!
这一道题:https://www.luogu.org/problemnew/show/P3383
标准代码:

#include <bits/stdc++.h>
using namespace std;
int n,w,ans[1000010],tot,s;
bool vis[100000010];
int main(){
    cin>>n>>w;
    for(int i=2;i<=n;i++){   
        if(vis[i]==false) 
           ans[++tot]=i;
        for(int j=1;j<=tot&&i*ans[j]<=n; j++) {
            vis[i*ans[j]]=true;
            if(i%ans[j]==0) 
               break;
        }
    }
    for(int i=1;i<=w;i++){
        cin>>s;
        int left=1,right=tot,mid;
        while(left<=right){ 
            mid=(left+right)/2;
            if(s==ans[mid]){ 
               cout<<"Yes"<<endl; 
               break; 
            }
            else if(s<ans[mid]) 
                right=mid-1;
            else 
                left=mid+1;
        }
        if(s!=ans[mid]) 
           cout<<"No"<<endl;
    }
    return 0;
}

这里显然就运用到了筛素数的进一步优化,就是将每一个质数以及它的倍数筛掉,比如说12就可以等于3*4,那么,12就显然不是质数,这样,将所有质数保存在一个数组里,最后只要查找一下就可以了,这真是太棒了!!!
那么,原题的进一步线性优化就是这样的:

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=1000+10,maxm=1000000+10;
int a[maxn],p[maxm],m;
void doing(){
    for(int i=2;i<=750;i++)
        if(!p[i]){
            a[++m]=i;
            for(int j=i*i;j<=maxm;j+=i)
                p[j]=1;
        }
}
int main(){
    int x;
    doing();
    while(1){
        cin>>x;
        if(x==0)
           return 0;
        for(int i=2;i<=x/2;i++)
            if(p[x-i]==0&&p[i]==0){
               cout<<x<<" = "<<i<<" + "<<x-i<<endl;
               break;
            }
    }
    return 0;
}

但是,这个程序还不是最优的,因为12既可以等于3*4也可以等于2*6,这样岂不是连续算了多遍??!!
那么,我们可以在原代码上进行进一步优化,如果能将重复算的部分删掉就好了,其实,是可以这样的,代码如下:

#include<bits/stdc++.h> 
using namespace std;
const int maxn=10000000+5; 
int p[maxn],a[maxn]; 
int main(){ 
    int n,m=0,k; 
    cin>>n>>k; 
    for(int i=2;i<=n;i++){ 
        if(!p[i])
           a[m++]=i;  
        for(int j=0;j<m;j++){ 
            if(i*a[j]>n) 
              break; 
            p[i*a[j]]=1; 
            if(i%a[j]==0) //这里是关键,如果能除尽,就说明这个数能被更大的数构成,就直接跳出循环。
              break; 
        } 
    } 
    for(int i=1;i<=k;i++){ 
        int s; 
        cin>>s;
        if(s==1){
           cout<<"No"<<endl;
           continue;
        } 
        if(p[s])
           cout<<"No"<<endl;
        else 
           cout<<"Yes"<<endl;
    } 
    return 0; 
}

猜你喜欢

转载自blog.csdn.net/qq_42875611/article/details/81635991
今日推荐