生活随笔
收集整理的這篇文章主要介紹了
硬币问题——固定终点的最长路和最短路
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
問題描述:
?? ??? 有n種硬幣,面值分別為V1,V2...,Vn,每種都有無限多。給定非負(fù)整數(shù)S,可以選用多少個(gè)硬幣,使得面值之和恰好為S?輸出硬幣數(shù)目的最小值和最大值。0 <= n <= 100, 0 <= S <= 10000, 1 <= Vi <= S。
分析:
?? ??? 本題的本質(zhì)還是DAG上的路徑問題。我們把每種面值看作一個(gè)點(diǎn),表示"還需要湊足的面值",則初始狀態(tài)為S,目標(biāo)狀態(tài)為0。若當(dāng)前的狀態(tài)i,每使用一個(gè)硬幣j,狀態(tài)便轉(zhuǎn)移到i-Vj。這個(gè)模型和嵌套矩形一題類似,但也有些明顯的不同之處:嵌套矩形中并沒有確定路徑的起點(diǎn)和終點(diǎn)(可以把任意矩形放在第一個(gè)和最后一個(gè)),而本題的起點(diǎn)必須是S,終點(diǎn)必須是0。把終點(diǎn)固定之后"最短路"才是有意義的。在嵌套矩形中,最短序列顯然是空(如果不允許空的話,就是單個(gè)矩形,不管怎樣都是平凡的),而本題的最短路徑卻不是那么容易確定的。
?????? 接下來考慮"硬幣問題"。注意到最長路和最短路的求法是類似的,下面只考慮最長路。由于終點(diǎn)固定,d(i)的確切含義變?yōu)?#34;從節(jié)點(diǎn)i出發(fā)到節(jié)點(diǎn)0的最長路徑長度"。
下面是求最長路的代碼(錯(cuò)誤的):
int dp(int S)//錯(cuò)誤
{int& ans=d[S];if(ans>=0) return ans;ans=0;for(int i=1;i<=n;i++)if(S>=V[i])ans=max(ans,dp(S-V[i])+1);return ans;
}
/*此代碼有一個(gè)致命的錯(cuò)誤,即由于節(jié)點(diǎn)S不一定真的能到達(dá)節(jié)點(diǎn)0,所以需要用特殊的d[S]值表
示"無法到達(dá)",但在上述代碼中,如果S根本無法繼續(xù)往前走,返回值是0,將被誤以為是"不能
走已經(jīng)到達(dá)終點(diǎn)"的意思。*/
正確的解法一:
int dp(int S)
{int& ans=d[S];if(ans != -1) return ans;ans=-1<<30;for(int i=1;i<=n;i++)if(S>=V[i])ans=max(ans,dp(s-V[i])+1);return ans;
}
注意:在記憶化搜索中,如果用特殊值表示"還沒有算過",則必須將其和其他特殊值(如無解)區(qū)分開。
正確的解法二:
int dp(int S)
{if(vis[S]) return d[S];vis[S]=1;int& ans=d[S];ans=-1<<30;for(int i=1;i<=n;i++)if(S>=V[i]){ans=max(ans,dp(S-V[i])+1);} return ans;
}
?????? 盡管多了一個(gè)數(shù)組,但可讀性增強(qiáng)了許多:再也不用擔(dān)心特殊值之間的沖突了,在任何情況下,記憶化搜索的初始化都可以用memset(vis,0,sizeof(vis))執(zhí)行。
????? 本題要求最小、最大兩個(gè)值,記憶化搜索就必須寫兩個(gè)。在這種情況下,還是遞推來得更加方便(此時(shí)需注意遞推的順序):
min[0]=max[0]=0;
for(int i=1;i<=n;i++)
{min[i]=INF; max[i]=-INF;
}
for(int i=1;i<n;i++)for(int j=1;j<=v;j++)if(i>=V[j]){min[i]=min(min[i],min[i-V[j]]+1);max[i]=max(max[i],max[i-V[j]]+1);}
printf("%d %d\n",min[S],max[S]);
完整代碼:
#include "stdio.h"
#define INF 1<<30
#define maxn 100+10
int V[maxn],n;
int min[maxn],max[maxn];inline int Min(int a,int b){return a<b?a:b;}
inline int Max(int a,int b){return a>b?a:b;}//打印可行的方案
void print_ans(int* d,int S){for(int i=1;i<=n;i++){if(S>=V[i] && d[S]==d[S-V[i]]+1){printf("%d ",V[i]);print_ans(d,S-V[i]);break;}}
}int main()
{int S;//輸入面值S和面值的種數(shù)n while(~scanf("%d%d",&S,&n)){ for(int i=1;i<=n;i++){scanf("%d",&V[i]);}max[0]=0; min[0]=0;for(int i=1;i<=S;i++){min[i]=INF; max[i]=-INF;}//遞推實(shí)現(xiàn) for(int i=1;i<=S;i++){for(int j=1;j<=n;j++){if(i>=V[j]){min[i]=Min(min[i],min[i-V[j]]+1);max[i]=Max(max[i],max[i-V[j]]+1);}}}print_ans(min,S); printf(" min\n");print_ans(max,S); printf(" max\n");printf("min:%d max:%d\n",min[S],max[S]); }return 0;
}
總結(jié)
以上是生活随笔為你收集整理的硬币问题——固定终点的最长路和最短路的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。