【暑期培训测试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 }

猜你喜欢

转载自www.cnblogs.com/hualian/p/13387218.html
今日推荐