P1021 郵票面值設計
0.總結
Get to the points first. The article comes from LawsonAbs!
- dfs+dp;
- 注意這種問題
dp
數組含義的令法很關鍵,我數次將這個dp[i]
令成價值i是否可達。而真正的應該設成:到達價值i
的最少需要的票數; - 在
dfs
中的dp
數組需要置零,因爲是根據每次的選擇都要重新計算;
1.題意
給出郵票數限制,郵票面值個數限制,請你推算出在合理的限制下信封可達到的最大郵票面值。
2.分析
2.1思路
dfs+dp
的題
2.2主要步驟
設dp[i]表示到達i需要最少郵票的數目
分析:
(1)最多能夠貼N張郵票
(2)有k種面值
(3)票值1肯定是必須存在的。
(4)基本數據結構:使用一個數組cho[]用於存儲選擇的票值。用dp[i]表示到達價值i的最小值
針對上面的分析,就可以用dfs深搜窮舉每次dp得到的票值上限—“dp過程中可以到達的最大值”,其下限則是“比當前選擇的數大1的數”。
然後按照上述過程找出最優解即可。
3.代碼
#include<iostream>
using namespace std;
const int N = 10000;
const int INF = 0x3f3f3f;
int n,k;//允許貼的郵票數;面值數
int cho[N],dp[N];//cho[i]表示需要使用到的數組;dp[i]表示到達i的最小郵票使用數量
int res = 0,cnt = 1;//最後的結果; 已經選了多少個數,初始爲1
int ans[N];//結果數組
void dfs(int low,int up){//下限,上限
if(cnt == k){//如果已經選滿了k個
if(res < up-1){
res = up -1;
for(int i = 0;i< cnt;i++){//複製整個數組
ans[i] = cho[i];
}
}
return ;
}
for(int i = low+1; i<=up;i++){
cho[cnt] = i;
cnt++;
//dp計算值
fill(dp,dp+N,INF);//重置
dp[0] = 0;
for(int l = 0;l < cnt;l++){//放前l個郵票
for(int j = cho[l] ; j <= n*cho[cnt-1]; j++){//
if(dp[j-cho[l]] != INF){//如果l-cho[j]可達
dp[j] = min(dp[j],dp[j-cho[l]]+1);
}
}
}
int temp;
//找出dp到達的最大值
for(int j = 0;j<=n*cho[cnt-1]+1;j++){
if(dp[j] > n){
temp = j;
break;//找出上限值
}
}
dfs(i+1,temp);
cnt--;//清零
}
}
int main(){
cin >> n >> k;
//初始化
cho[0] = 1;
fill(dp,dp+N,INF);
dp[0] = 0;
dfs(1,n+1);//[1,n]
for(int i = 0;i< k;i++){
cout << ans[i]<<" ";
}
cout <<"\nMAX="<< res <<"\n";
}