uva 10313 整數拆分——硬幣湊價
阿新 • • 發佈:2019-02-16
給你一個價格n,求在指定使用的硬幣個數範圍內付款的方案數,後面可能給你邊界a,b,也可能不給,不給的話個數是1-n,給一個a就是1-a,給兩個是a-b
直接開二維陣列揹包的話會算重複,比如湊6的話,1、2、3,,2、1、3,3、1、2,一種方案就數了三遍,需加一維限制大小,dp[i][j][k]中用j個硬幣湊成價格i,j個硬幣中最大的硬幣面值最大是k,dp[i][j][k]+=dp[i-k][j-1][t], 1<=t<=k.但這樣規模會太大
這個題目涉及到一個結論,用不超過j個硬幣湊出面值i的方案種數,是和用面值不超過j的硬幣湊出面值i的方案種數是相同的。說得再數學一點,就是整數i拆分成不超過j個整數的拆分數,是和整數i拆成若干個值不超過j的整數的拆分數是相同的。具體的證明用到了Ferrers影象的性質。
這樣的話我們就可以取一個二維陣列f[i][j]表示用面值不超過j的硬幣湊出面值i的方案的種數,那麼如果我使用了面值j,對應方案種數就應該加上f[i-j][j],如果我們不使用面值j,那麼對應的方案種數就應該加上f[i][j-1]。也就是說狀態轉移方程為f[i][j]= f[i-j][j]+ f[i][j-1]。
百度百科#include<stdio.h> #include<string.h> #include<iostream> #define N 310 using namespace std; long long f[N][N]; int n,a,b; char s[200]; int main() { int i,j,k,l; memset(f,0,sizeof(f)); /* for(i=0;i<=300;i++) f[0][i]=1; for(i=0;i<=k;i++) { for(j=1;j<=i;j++) { if(i>=j) f[i][j]+=f[i-j][j]; //用j f[i][j]+=f[i][j-1];//沒用j } } 這樣不對,j比i大的時候,f[i][j]還是0,即每次用了j時f[i][j]加上的情況f[i-j][j]一直是0,少算了,f[i][j],當i比j小的時候依然有用 */ f[0][0]=1; k=300; for(i=0;i<=k;i++) { for(j=1;j<=k;j++) { if(i>=j) f[i][j]+=f[i-j][j]; //用j f[i][j]+=f[i][j-1];//沒用j } } while(gets(s)!=NULL) { a=b=-1; sscanf(s,"%d%d%d",&n,&a,&b); //sscanf中沒有讀取成功將不會改變原值 if(a==-1&&b==-1) a=1,b=n; else if(a!=-1&&b==-1) b=a,a=1; printf("%lld\n",f[n][b]-f[n][a-1]); } return 0; }
Ferrers影象
影象概念
一個從上而下的n層格子,mi 為第i層的格子數,當mi>=mi+1(i=1,2,,n-1) ,即上層的格子數不少於下層的格子數時,稱之為Ferrers影象。[1]影象性質
(1)每一層至少有一個格子;(2)第一行與第一列互換,第二行與第二列互換,…,所得到的圖象仍然是Ferrers圖象,這兩個 Ferrers圖象稱為是一對共軛的Ferrers圖象。
性質(2)