最近练的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 }