代数部分小结

代数部分小结
问题:正整数集合N,最多有数字1000个,给定一个正整数K
从N中取任意个数字相加,和为SUM,SUM不大于K
求SUM可以达到的最大值
题解:这是一个比较典型的01背包问题,可以用动态规划的方法来解决.
首先,对问题进行一点小小的变形,即将K看做背包容量v,而每一个集合中的数字,看作一件物品,它的重量c和价值w均为其数值本身.
然后,用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值.则其状态转移方程便是:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
至此,即可编写程序依照上面的状态转移方程对f[N][K]进行求解了.
如果要对求解的过程进行优化,可以参考01背包问题的具体讲解
HDU1262:寻找素数对
输入一个偶数,输出两个彼此最接近的素数,其和等于该偶数.
思路:打表,然后从n/2开始枚举到0即可。
HDU1215:七夕节
输入N,输出因子和(所有比N小又能被N整除的所有正整数,如12的因子有1,2,3,4,6.)
思路:打表秒杀for(int i=1; i<=MAXN>>1; ++i) for(int j=i+i; j<=MAXN; j+=i) a[j] += i;
问题:求1到N互质数对
思路:PH欧拉打表,ANS递推ans[i]=ph[i]+ans[i-1]即可
SCOI2010:生成字符串(组合数 + 思维)
问n个1和m个0组成的合法字符串个数,要求前k个字符中,1的个数不少于0的个数
思路:建立二维坐标,初始在(0,0)点,目标在(n+m,n-m)点,每次只可以走右上或者走右下,分别代表选1和选0,不能走到第四象限,否则不满足任何前缀1不比0少。答案是总方案数减去非法方案数,观察到每个非法方案都经过y=-1线,根据对称性,第一次到达y=-1的前面往下翻折,就相当于从(0,-2)出发到(n+m,n-m)。对于总方案数显然是C(n+m,n)。考虑非法方案数从(0,-2)出发,向右上走的就有n-m+2+[n+m-(n-m+2)]/2 = n+1步,那么非法方案数就是C(n+m,n+1),模数是质数,可选择逆元推组合数。
hdu 1717 小数化分数2
将小数化成分数,小数包括普通小数(有限小数)和无线循环小数。
题解:普通小数,乘以10^k后变为整数,小数a*10^k=b(b为整数),得a=b/10^k,通分。
再是无线循环小数,以0.148257148257...为例,化去无线循环部分,使它变为普通小数。
它的循环部分为:148257, 长度为6 , 在等式:x=0.148257148257...的左右两边各乘以10^6, 得到: 1000000x=148257.148257148257...,然后减去式子x=0.148257148257...,
得到: 999999x=148257,即x=148257/999999=1/7
Hdu1452 Happy 2004
求2004^x因子和mod29
满足gcd(a,b)==1 && s(a*b)==s(a)*s(b)的s叫做积性函数,本题求的因子和就是一个积性函数
接着有一个结论if(prime[p])s(p^n)=1+p^1+p^2+p^n=(p^(n+1)-1)/(p-1)
易得,s(2004^n)=s(2^(2n))*s(3^n)*s(167^n) 其中167和22关于29同余
所以,s(2004^n)=s(2^(2n))*s(3^n)*s(22^n)
a=s(2^(2n))=(2^(2n+1)-1),b=s(3^n)=(3^(n+1)-1)/2,c=s(22^n)=(22^(n+1)-1)/21
数太大,每步求余,除以一个数求余的结果和乘以除数的乘法逆元的求余结果相同
求出2和21的乘法逆元这道题就做完了
问题:给出一个N,求1..N中与N互质的数的和
思路:sigma (i=1...n) i*[gcd(i,n)==1]
反证法:gcd(n,i)=1
如果存在K!=1使gcd(n,n-i)=k,那么(n-i)%k==0且n%k=0
那么必须保证i%k=0。
i%k==0&&n%k==0 那么gcd(n,i)=k,与已知条件矛盾,所以不成立。  
得到结论gcd(n,i)=1 则gcd(n,n-i)=1 ,故i,n-i总是成对出现并且和是n。
于是问题变的非常简单:ANS=N*phi(N)/2
那如果n-i=i会不会重复计算呢?
分类讨论
1.如果n是奇数,那么n!=2*i,自然也不存在 n-i=i和重复计算之说
2.如果n是偶数,n=2*i成立,gcd(n,n/2)必然为n的一个因子,这个因子为1当且仅当n==2
于是对于n>2的偶数,gcd(n,n/2)=n/2。对于n==2,ans=2*1/2=1,正好也满足
所以得到最终公式:   ans=N*phi(N)/2 

sdibt 2406 Greatest Number (dfs)
题意:给N个数,选不超过4个数计算(可重复选取)其和,但和不能超过M,求最大和
#include<bits/stdc++.h>
using namespace std;
int a[1010],n,m,s;
int cmp(int a,int b){return a>b;}
void dfs(int i,int sum,int gs){//i为将要计算的数组元素的下标,sum为和,gs为将要加第几个数
    if(sum>s)s=sum;//当计算的和比最大值大,将和的最大值更新
    if(i>=n||sum==m||gs>4||sum+a[i]*(5-gs)<=s)return;//5-gs表示当前还需要加几个数,若sum+a[i]*(5-gs)比最大值小,则接下来的数比a[i]更小故最后和肯定比最大值也小
    if(sum+a[i]<=m)dfs(i,sum+a[i],gs+1);//当前和加上a[i]后未超过上限,下一轮仍选第i大的数累加,本轮临时和累加a[i]传给下一层,下一次选是第GS+1个数
    if(sum<=m)dfs(i+1,sum,gs);//当前和未超过上限,本轮不选当前这个数,和没有累增,下一次选的数依然是第GS个
}
int main(){
    int i,k=1;
    while(scanf("%d%d",&n,&m)!=EOF){//输入N个数及限定和M
        if(n==0&&m==0)break;//结束条件
        for(i=0;i<n;i++)scanf("%d",&a[i]);//输入N个数
        sort(a,a+n,cmp);//降序排列
        s=a[n-1];//初始化最大值为最小的数
        dfs(0,0,1);//从第0个数即最大数开始,当前传入的已积累的和=0,即将要加的数是第1的数
        printf("Case %d: %d\n\n",k++,s);//第K个询问的答案是S
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cj1064789374/article/details/85219035