1. 程式人生 > >代數部分小結

代數部分小結

代數部分小結
問題:正整數集合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;
}