筛法函数
筛法函数,我是在寻找约数和的时候发现的
什么是筛法函数?
筛法函数的作用?
就比如求前n个数的每一个数的约数和(约数不含这个数本身)
例如:6的约数和为 1+2+3=6,4的约数和为 1+2=3
筛法函数可以用在,寻找前n个数的约数和
这是常规的筛法函数求前n个数的质数:
bool s[10005]={1,1};//0和1啥也不是,定为1
int a[10005],ps;//a数组存最后的质数,ps为这个数组的下标
//全局数组初值全为0
void sf(){//筛法函数
for (int j=2;j<10000;j++)//这里的j的含义就是约数
if (!s[j]){//s[j]=0,表明j不是合数(合数标记为1)
a[ps++]=j;//因为2是质数,所以先存入2这个质数,数组下标+1
for (int k=j*2;k<10000;k+=j)//k=j*2,因为j是约数,所以j*2明显也是约数,每次+j,保证是j的倍数
//这里的每一个k都是约数,因为可以被j整除!
s[k]=1;//k一定是合数,标记!
}
}
//最后s数组中标1的数为合数,0为质数,s数组可以找出前10000个数中的质数和合数分别是谁
//数组a即为前10000的每一个质数
//时间复杂度约为n
通过简单的变形
void prime(){
for (int i=1;i<=n;i++)//i为每一个约数
for (int j=i*2;j<=n;j+=i)//j也是约数
//比如i=1时1*2=2,2的约数有1
// 2+1=3,3+1=4 ...(n-1)+1=n 每一个数的约数都有1
//i=2时 2*2=4,4的约数有2
// 2+2=4,4+2=6... 每一个j都有约数=2
a[j]+=i;
}
数组a[j]就是数j的约数和
void Mya(){
for(int i=2;i<=n;i++){
for(int j=1;j<=i/2;j++){
if(i%j==0) a[i]+=j;
}
}
}
这是我本来写的代码,好像时间复杂度更高一点…好头大啊0.0,自己也解释不清了,那就暂时先记住吧,以后用得到的时候再来补充…
背包!
背包刚开始学习好像都是在教用二维数组解决,01背包:
#include <bits/stdc++.h>
using namespace std;
int T,M;
int s[105],v[105];
int dp[105][1005];
int Max(int a,int b){
return a>b?a:b;
}
int main()
{
cin>>T>>M;
for(int i=1;i<=M;i++){
cin>>s[i]>>v[i];
}
for(int i=1;i<=M;i++) dp[i][0]=0;
for(int i=1;i<=T;i++) dp[0][i]=0;
for(int i=1;i<=M;i++){
for(int j=1;j<=T;j++){
if(j>=s[i]){
dp[i][j]=Max(dp[i-1][j],dp[i-1][j-s[i]]+v[i]);
}else{
dp[i][j]=dp[i-1][j];
}
}
}
cout<<dp[M][T];
return 0;
}
这个图是倒着推的,上面代码是正着推的…
另外还有一维数组解决背包问题的办法
#include <bits/stdc++.h>
using namespace std;
int w[105], val[105];
int dp[1005];
int main()
{
int t,m;
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&w[i],&val[i]);
}
for(int i=1;i<=m;i++)
{
for(int j=t;j>=0;j--)
{
if(j>=w[i])
{
dp[j]=max(dp[j-w[i]]+val[i], dp[j]);
}
}
}
printf("%d",dp[t]);
return 0;
}
一维数组解决背包问题的注意点就是内层循环要倒着来
for(int i=1;i<=m;i++)
{
for(int j=t;j>=0;j--)
{
if(j>=w[i])
{
dp[j]=max(dp[j-w[i]]+val[i], dp[j]);
}
}
}
如果不倒着来,可能会造成重复放同一个物品的情况,比如第一个物品重量为5,背包容量为10,就会有
i=1:
dp[5] = max(dp[0]+20, dp[5]);
dp[6] = max(dp[1]+20, dp[6]);
dp[7] = max(dp[2]+20, dp[7]);
dp[8] = max(dp[3]+20, dp[8]);
dp[9] = max(dp[4]+20, dp[9]);
dp[10] = max(dp[5]+20, dp[10]);
这里的dp[10]要表达的含义是:背包容量为10的时候,放不放该物品,如果要放该物品,那么背包的容量就需要减去该物品的体积,也就是10-5=5,括号里面的dp[5]的含义应该是,背包容量为5的时候,只考虑上一个物品之前的物品的最优解,但是dp[5]在开始的时候却被赋予了新的值,此时dp[5]表示的是已经放了该物品
此处的dp[5]是新值,不是旧值!
dp[10]进行了重复放入
dp[i]中的i是增加的,括号中dp[j]的j也是增加的,由于j<i,所以会出现重复放入的情况
如果是倒着来
i=1:
dp[10] = max(dp[5]+20, dp[10]);
dp[9] = max(dp[4]+20, dp[9]);
dp[8] = max(dp[3]+20, dp[8]);
dp[7] = max(dp[2]+20, dp[7]);
dp[6] = max(dp[1]+20, dp[6]);
dp[5] = max(dp[0]+20, dp[5]);
这样一来,括号内的值就全都是旧值了!
一维数组解决背包问题的最优代码:
#include <bits/stdc++.h>
using namespace std;
int w,val;
int dp[1005];
int main()
{
int t,m;
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&w,&val);
for(int j=t;j>=w;j--)
{
dp[j]=max(dp[j-w]+val, dp[j]);
}
}
printf("%d",dp[t]);
return 0;
}
在输数据的时候进行dp,即节省了空间,又加快了运行速率,这好像叫,状态压缩?现在还不太懂,以后用到的话再详细研究
背包题目
题目描述
选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。
输入格式
输入一个正整数S。
输出格式
输出最大的约数之和。
其实这道题很容易就能想到背包(连我都能很容易想到…)
dp[j]=Max(dp[j],dp[j-i]+a[i]);
很容易就能想到吧,i表示小于S的数,01背包,取不取这个数,如果取了那么容量S就减i,否则就等于 旧值,但还是做了一点变形…导致我还是搜了题解才做了出来…被自己菜哭了 || _||
void solve(){
for(int i=2;i<=S;i++){//i表示需要判断01的数,也就是取不取的数..
for(int j=i;j<=S;j++){
dp[j]=Max(dp[j],dp[j-i]+a[i]);
}
}
}
大概就是…这样吧…
其实我还联想到一道题,就是最长增长子序列
for(int i=1;i<num;i++){
for(int j=0;j<i;j++){
if(a[j] <= a[i]){
dp[i]=max(dp[i],dp[j]+1);
}
}
}
挺像的吧…这个也是先固定一点,固定的点表示求该点之前的最长增长子序列的长度,然后对其前面的点进行判断,如果前面的值比这个固定的点小,那么就在旧值和新值之间取最大值,因为这里的j总是小于i,所以不存在重复判断的情况…就是前面重复放入同一个物品那样的情况…
感觉就是那么回事,但是又搞不很清楚里面的原理…