A:The Way to Home
link:http://codeforces.com/contest/910/problem/A
题面:有每次最大跳跃距离d,只有一部分的点可以落脚,求最少几步达到终点D
Solution :预处理+贪心
用f[i]表示离点i最近的可落脚点,贪心即可(dp同理)
#include <bits/stdc++.h> using namespace std; int n,d,pre[105]; string s; int main() { cin >> n >> d >> s; n--; pre[0]=0; for(int i=1;i<s.size();i++) if(s[i]=='1') pre[i]=i; else pre[i]=pre[i-1]; int cur=0,res=0; bool f=true; while(cur<n) { if(pre[min(cur+d,n)]==cur) { f=false; break; } cur=pre[min(cur+d,n)]; res++; } if(!f) cout << -1; else cout << res; return 0; }
B. Door Frames
http://codeforces.com/contest/910/problem/B
题面:有4个长度为a和2个长度为b的木板,求最少用多少个长度为n的木板可以裁出所要求的所有木板
Solution A:分析法
这也是我用的方法,因为只有2个长度为b的木板,因此只有两种情况,一个n做2个b,或2个n做2个b,其余空间全部由a填满。
Tip:c++中自带的上下取整函数:floor()和ceil()
#include <bits/stdc++.h> using namespace std; typedef pair<float,float> P; int n,a,b; int main() { cin >> n >> a >> b; if(n>=(a+a+b)*2) cout << 1; else if(n>=(a+a+b)) cout << 2; else { P n1,n2,n3; int res=1e9; n1.first=floor(n/a);n1.second=0; if(n>=2*b) { n3.first=floor((n-2*b)/a); n3.second=2; res=min(res,1+(int)ceil((4-n3.first)/n1.first)); } if(n>=b) { n2.first=floor((n-b)/a); n2.second=1; res=min(res,2+(int)ceil((4-2*n2.first)/n1.first)); } cout << res; } return 0; }
Solution B:
因为真正会影响最终结果的是裁剪的顺序,因此对6个数全排列即可
在考虑最优解问题时,可以从枚举出每种可能的顺序下手
Tip:next_permutation()如一开始不排序,最终枚举的是该序列之后的排列
#include <bits/stdc++.h> using namespace std; #define pb push_back int main() { int n,a,b,res=1e9; cin >> n >> a >> b; vector<int> v; for(int i=0;i<4;i++) v.pb(a); v.pb(b);v.pb(b); sort(v.begin(),v.end()); do { int l=n,cur=1; for(int i=0;i<6;i++) if(l-v[i]>=0) l-=v[i]; else cur++,l=n-v[i]; res=min(res,cur); } while(next_permutation(v.begin(),v.end())); cout << res; return 0; }
Solution C:递归
这也是我一开始未想到的,其实本应该是非常直观的思路
用dfs(x,y)返回还剩x个a,y个b最少要用几个n来组成,直接暴力
Tip:当有两层循环且内层循环当i=0时j要从1开始时,可以将起始条件设为j=(i==0)
#include <bits/stdc++.h> using namespace std; int n,a,b; int dfs(int x,int y) { int ret=10; if(!x && !y) return 0; for(int i=0;i<=x && i*a<=n;i++) for(int j=(i==0);j<=y && j*b+i*a<=n;j++) { ret=min(ret,dfs(x-i,y-j)); } ret++; return ret; } int main() { cin >> n >> a >> b; cout << dfs(4,2); return 0; }
Solution D:状压DP
Zscoder大神的写法,虽然没有必要,但代码技巧是值得学习的
先用only数组储存所有一个n能组成的情况,接下来进行状压DP
Tip: 1、当判断在二进制下i是否包含j时,使用(i&j)==j
2、当表示在二进制下只在i中但不在j中的1时,使用i^j(前提:i包含j,否则表示的是两者不共同所有的1)
#include <bits/stdc++.h> using namespace std; #define pb push_back int dp[(1<<10)]; int n,a,b; int main() { cin >> n >> a >> b; vector<int> v; for(int i=0;i<4;i++) v.pb(a); for(int i=0;i<2;i++) v.pb(b); for(int i=0;i<(1<<6)+10;i++) dp[i]=1e9; dp[0] = 0; vector<int> only; for(int i=1;i<(1<<6);i++) { int sum=0; for(int j=0;j<v.size();j++) if(i&(1<<j)) sum+=v[j]; if(sum<=n) { dp[i]=1;only.pb(i); } } for(int i=1;i<(1<<6);i++) for(int z=0;z<only.size();z++) { int j=only[z]; if((i&j)==j) dp[i]=min(dp[i],dp[j]+dp[(i^j)]); } cout<<dp[(1<<6)-1]; }
C:Minimum Sum
link:http://codeforces.com/contest/910/problem/C
Solution:
先计算出每个字母要计算的总次数,同时记录其是否有可能为首位,接下来贪心
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<ll,bool> P; int n; P a[30]; int main() { cin >> n; for(int i=1;i<=n;i++) { string s;cin >> s; ll time=1; for(int j=s.size()-1;j>=0;j--) { if(!j) a[s[0]-'a'].second=true; a[s[j]-'a'].first+=time; time*=10; } } sort(a,a+10,greater<P>()); ll res=0,cur=1; bool used=false; for(int i=0;i<=9;i++) { if(!a[i].second && !used) used=true; else { res+=a[i].first*cur; cur++; } } cout << res; return 0; }