(DAG+固定终点的最长路和最短路)硬币问题
##題目:
有n種硬幣,面值分別為v1, v2, …, vn,每種都有無限多。給定非負整數S,可以選用多少個硬幣,使得面值之和恰好為S?輸出硬幣數目的最小值和最大值。
Input
第一行兩個整數,n,S(1≤n≤100, 0≤S≤100000)。
第二行n個整數vi-1…n(1≤vi≤S)。
Output
第一行兩個整數,分別表示硬幣數目的最小值 a 和最大值 b 。無解則輸出 -1 。
第二行 a 個整數分別表示使用的是第幾種硬幣。
第三行 b 個整數分別表示使用的是第幾種硬幣。
Sample Input
6 12
1 2 3 4 5 6
Sample Output
2 12
6 6
1 1 1 1 1 1 1 1 1 1 1 1
##分析與解
假如s是12,怎么弄個金錢加加減減就到十二呢
假設每個結點表示剩的錢數
怎么和DAG想到一塊,可以這么想,現在已知一個路的起點s和終點0,每多加一個錢,s就變到另一個數s2,現在找s2和終點0的路,這樣就成了一個DAG
##思考
我們用遞推,想找剩下s元到剩下零元的最小使用錢的次數
現在找最短路徑的話
遞推公式:dmin[i]=min(dmin[i],dmin[i-v[j]]+1)
我們初始化dmin[0]=0剩下零元到零元不需要錢,次數為零
dmin[i]的話就是看dmin[i]到零元的最小使用錢次數,他需要和dmin[i-v[j]]+1比較,其中+1是說dmin[i-v[j]]加上v[j],使用了dmin[i-v[j]]加上一次使用v[j]的次數
我們可以發現dmin[s]可以由dmin[0]向上推出
那不妨把遞推公式轉換成迭代
如果剩下的錢i減去v[j]大于零,說明可以通過dmin[i-v[j]]+1來確定dmin[i]
那么假設我們的dmin[i]就等于dmin[i-v[j]]+1,并且dmin[0]等于0,所以這樣一步一步推出dmin[s]
由于題目說字典序,我們再寫最大最小值時候也是遇見大于max的就存標號,更改max,后面來個等于max的,我們不能再存標號,因為我們要的是第一個數,所以后面來的必須要大于max,我們才改標號
這個也是一樣的道理,必須有新的dmin[i-v[j]]+1比之前的dmin[i-v[j2]]+1小的時候我們替換掉dmin[i]的值。
由于要考慮輸出,輸出的都是v[j],那我們就要記錄每一個i他減的v[j]是什么,就是當dmin[i]>dmin[i-v[j]]+1時,我們不僅替換dmin[i]=dmin[i-v[j]]+1,還要替換path_min[i]=j,path_min[i-v[j]]=j2,如果i-v[j]=0,此時說明v[j]+v[j2]=s
我這輸出沒排序,如果要排序的話,存到數組里,然后輸出
int V[maxN];i是第幾種,v[i]是第i種的價值
int Dmin[maxS];dmin[i]是i到0所需的最小路徑
int Dmax[maxS];
int path_min[maxS];path_min[i]是最小路徑中由i走到下一個結點所減去的那個數v[j]的下標j
int path_max[maxS];
總結
以上是生活随笔為你收集整理的(DAG+固定终点的最长路和最短路)硬币问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux中split函数用法,Linu
- 下一篇: java8 foreach 异常_错误处