洛谷P1021 郵票面值設計 題解
阿新 • • 發佈:2020-11-05
首先,看到這題,大家肯定首先想到暴力+dfs吧!
可是這題暴力會超時;
好吧我們還是來認真思考下正解
思路應該是枚舉出所有種類的郵票,最後判斷一下,並記錄最大值
暴搜,不行的話,可以剪枝?
1.使a陣列保持單調遞增,dfs中每次從a[k-1]+1開始搜尋,以此來消除重複的搜尋;(常規思路)
2.a[1]=1;//每次肯定都要1這個面值的,可以思考一下,那麼直接從a[2]開始列舉;
思考一個問題,dfs的列舉的下界已經清晰,可是上界該是多少呢?
這裡我們來舉個栗子吧~
假設當前準備填第k個(已經填好了k-1)個
前k-1箇中可以湊出1-t中的所有整數(需要用dp求出t)
因此我們可以把上界定為t+1(而不是t,因為超過t+1就湊不出t+1,答案就沒法+1了)
也就是說
for(int j=a[k-1]+1;j<=end+1;j++){ a[k]=j;dfs(k+1);a[k]=0; }
//這裡的end就是由dp求出來前k-1湊出的答案;
誒?怎麼突然有了個dp出來(因為我們不知道前面的答案)
於是想到一個dp[j]表示a陣列湊成j面值個數的最小值;
於是很容易得到
dp[j]=min(dp[j],dp[j-a[i]]+1);(dp[j-a[i]]<n)//顯然現在的情況是小於等於n的;
初始化+oo,dp[0]=0;
code來啦!
#include<bits/stdc++.h> using namespacestd; int n,m,maxx=0; int dp[51000],ans[25],a[25]; int solve(int k){ memset(dp,63,sizeof(dp));dp[0]=0; for(int i=1;i<=k;i++)//前k個數 for(int j=a[i];j<=a[k]*n;j++)//a陣列保持其單調性,最多能組成到a[k]*n,此時全部都選最大的數 if(dp[j-a[i]]<n)//只能繼承的<n dp[j]=min(dp[j],dp[j-a[i]]+1);//求最小值int x=0; while(dp[x+1]<=100)x++;//統計結果 return x; } void dfs(int k){ if(k==m+1){//如果已經找到m個 int t=solve(k-1); if(t>maxx){ maxx=t; for(int i=1;i<=m;i++) ans[i]=a[i]; } return; } int end=solve(k-1); for(int j=a[k-1]+1;j<=end+1;j++){ a[k]=j; dfs(k+1); a[k]=0; //很重要的暴力列舉 } } int main(){ cin>>n>>m; a[1]=1; //不管怎樣都會選1; dfs(2); for(int i=1;i<=m;i++)printf("%d ",ans[i]); printf("\nMAX=%d\n",maxx);//愉快輸出答案! return 0; } //dp+dfs;