1. 程式人生 > 實用技巧 >【暑期培訓測試2】

【暑期培訓測試2】

最近練的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(int
i=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 }