【暑期培訓測試2】
阿新 • • 發佈:2020-07-27
最近練的DP,所以考試全部都是DP型別的(然而最後一道是個搜尋)
這個題,一看就是揹包好伐,首先做一次多重揹包,dp[i]求出湊成i元錢需要最少的硬幣的數量
然後在此基礎上做一個完全揹包(因為店主有無限多的硬幣)即可
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,t,MAX_V; 4 int c[110]; 5 int v[110]; 6 int dp[140010]; 7 inline int read() 8 { 9 int x=0; 10 char ch=getchar(); 11 while(ch>'9'||ch<'0')ch=getchar(); 12 while(ch>='0'&&ch<='9')x=x*10+(ch-'0'),ch=getchar(); 13 return x; 14 } 15 int main() 16 { 17 memset(dp,0x3f,sizeof dp); 18 dp[0]=0; 19 scanf("%d%d",&n,&t); 20 for(int i=1;i<=n;i++)v[i]=read(); 21 for(int i=1;i<=n;i++)c[i]=read();22 for(int i=1;i<=n;i++)//多重揹包,不用二進位制會WA 23 { 24 for(int k=c[i],m=1;k;m*=2) 25 { 26 if(m>k)m=k; 27 k-=m; 28 for(register int j=140000;j>=m*v[i];j--) 29 dp[j]=min(dp[j],dp[j-m*v[i]]+m); 30 } 31 } 32 for(inti=1;i<=n;i++)//完全揹包,因為店主是找錢,所以這裡是從大的更新小的 33 for(register int j=140000-v[i];j>=1;j--) 34 dp[j]=min(dp[j],dp[j+v[i]]+1); 35 if(dp[t]==0x3f3f3f3f)puts("-1"); 36 else printf("%d\n",dp[t]); 37 return 0; 38 }
典型的區間DP
dp[i][j]表示從i~j的區間需要用的最少的塗色數量
當新的一個元素加進來的時候,我們可以直接花費一次
也可以選擇利用之前已經畫過的更新過來(距離要小於k)
當這樣的時候,這段黃色的和剩下左邊的黑色的就是互相獨立的(因為如果有交集的話,交集的地方會更新兩次,是沒有意義的),就可以利用之前做過的更新過來
但是可能不止一個可以更新,所以遍歷一遍
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,c,m; 4 int a[420]; 5 int dp[420][420]; 6 int main() 7 { 8 memset(dp,0x3f,sizeof dp); 9 scanf("%d%d%d",&n,&c,&m); 10 for(int i=1;i<=n;i++) 11 { 12 scanf("%d",&a[i]); 13 a[i+n]=a[i];//斷環為鏈 14 } 15 for(int i=1;i<=2*n;i++)dp[i][i]=1;//初始化 16 for(int i=2*n;i>=1;i--)//一定注意列舉順序 17 { 18 for(int j=i+1;j<=2*n;j++) 19 { 20 if(j-i+1<=m&&a[i]==a[j])dp[i][j]=dp[i+1][j];//如果可以一起,新加進來的就不消耗次數 21 else dp[i][j]=dp[i+1][j]+1;//不然消耗一次 22 for(int k=i+1;k<=j;k++)//看能否根據之前更新 23 { 24 if(a[k]==a[i])//可以 25 { 26 if(k-i+1<=m)//就更新 27 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]); 28 else break; 29 } 30 } 31 } 32 } 33 int ans=1e9; 34 for(int i=1;i<n;i++)//去最小值輸出 35 ans=min(ans,dp[i][i+n-1]); 36 printf("%d",ans); 37 return 0; 38 }
直接搜,就硬搜
1 #include<bits/stdc++.h> 2 #define ull unsigned long long 3 using namespace std; 4 char s[110]; 5 int fin[110]; 6 int t,len; 7 int answer=1e9; 8 void dfs(int k,ull last,ull mul,ull ans,int sum)//第k位,最後一個數是last 另一個因數是mul 當前值是ans 用了sum個符號 9 { 10 if(k==len)ans+=mul*last;//如果最後一位了,就(深淵)結算 11 if(!fin[k]&&ans>t)return;//超了,並且後面沒有0,不能變小了,剪枝 12 if(ans==t&&k==len||sum>=answer)//滿足條件或者比當前最優解大 13 { 14 answer=min(answer,sum); 15 return;//退出 16 } 17 if(k==len)return;//退出 18 19 //加法就直接加,因數改為1, 20 if(ans<=t)dfs(k+1,(ull)(s[k]-'0'),1,ans+mul*last,sum+1); 21 22 //乘法 繼續累加,上一個因數乘上去 23 dfs(k+1,(ull)(s[k]-'0'),mul*last,ans,sum+1); 24 25 //無符號就*10加,其餘不變 26 dfs(k+1,last*10+(ull)(s[k]-'0'),mul,ans,sum); 27 } 28 int main() 29 { 30 while(1) 31 { 32 answer=1e9; 33 scanf("%s",s); 34 scanf("%d",&t); 35 len=strlen(s); 36 for(int i=len-1;i>=0;i--)//看剩下的數字中是否有0 37 { 38 fin[i]=!(s[i]-'0'); 39 fin[i]=fin[i+1]; 40 } 41 42 if(t==-1)break; 43 dfs(1,s[0]-'0',1,0,0); 44 if(answer==1e9)puts("-1"); 45 else printf("%d\n",answer); 46 } 47 return 0; 48 }