比赛主页:https://www.jisuanke.com/contest/7332
A. Wall Street Monopoly:
solution:
涉及知识点:区间dp
划重点:区间dp就是在区间上进行动态规划,求解一段区间上的最优解。主要是通过合并小区间的 最优解进而得出整个大区间上最优解的dp算法。
下面我们通过一个例子来了解一下这道题是如何用区间dp求解,:
如图所示,我们设dp[i][j]代表第 i 个物体到第 j 个物体相连的最小代价
设a[i] , b[i] 代表第 i 个物体的长和宽
初始化dp[1][1] = dp[2][2] = ... = dp[n][n] = 0,因为自身相连的代价是0
我们需要依次枚举相连的长度,长度等于2 :
dp[1][2] = dp[1][1] + dp[2][2] + min(a[1] , b[1])*min(a[2],b[2] )
dp[2][3] = dp[2][2] + dp[3][3] + min(a[2] , b[2])*min(a[3],b[3] )
dp[3][4] = dp[3][3] + dp[4][4] + min(a[3] , b[3])*min(a[4],b[4] )
那么长度为3的dp[1][3] 和 dp[2][4] 大家是否能自己写出来呢?
划重点:通项公式
为了便于理解,我们手写一下通向公式
dp[i][j] = min(dp[i][j] , dp[i][k] + dp[k+1][j] + 合并的代价)
那么区间dp的核心代码:
for(int len = 1; len <= n; len++){ for(int j =1; j + len <= n+1; j++){ int ends = j + len - 1; for(int i = j; i < ends; i++){ dp[j][lens] = min(dp[j][lens] , dp[j][i] + dp[i+1][ends] + something); } } }
显然这道题的问题就变成了求something是什么?
题目中明确指示,合并两个物体的代价是较小的边相乘,我们只需要模拟一下多个物体合在一起的长和宽,记录下最小值,相乘即可
std:
#include <bits/stdc++.h> using namespace std; #define ll long long const int maxn = 25; ll a[maxn],b[maxn]; ll dp[maxn][maxn]; int main() { int t; cin>>t; for(int cas = 1;cas <= t; cas++) { int n; scanf("%d",&n); memset(dp,0x3f3f3f,sizeof(dp)); for(int i=1;i<=n;i++){ scanf("%lld %lld",&a[i],&b[i]); dp[i][i] = 0; } ll k1 = 0,k2 = 0,k3 = 0,k4 = 0; for(int len=1;len<n;len++){ for(int i=1;i<=n;i++){ int j = i+len; if(j > n) continue ; for(int k=1;k<=len;k++){ k1 = k2 = k3 = k4 = 0; for(int z = i;z<=i+k-1;z++){ k1 += a[z]; k2 = max(k2 , b[z]); } for(int z=i+k;z<=j;z++){ k3 += a[z]; k4 = max(k4 , b[z]); } dp[i][j] = min(dp[i][j] , dp[i][i+k-1] + dp[i+k][j] + min(k1,k2)*min(k3,k4)); } } } printf("The minimum cost for lot #%d is $%lld.\n",cas,1ll*100*dp[1][n]); if(cas != t){ printf("\n"); } } return 0; }
B. Wall Street Monopoly:
solution:
涉及知识点:字符串的字典序
从‘z’-‘a’比较两个字符串拥有的个数即可,注意输出
std:
#include <bits/stdc++.h> using namespace std; #define ll long long map<char ,int> mp1 , mp2; int main() { int n; cin>>n; for(int cas = 1;cas <= n;cas++) { int flag = 0; mp1.clear(),mp2.clear(); string s1,s2; cin>>s1>>s2; for(int i=0;i<s1.length();i++){ mp1[s1[i]]++; } for(int i=0;i<s2.length();i++){ mp2[s2[i]]++; } for(int i=25;i>=0;i--){ char c = i+'a'; if(mp1[c] == mp2[c]){ continue ; } if(mp1[c] > mp2[c]){ flag = 1; break ; } if(mp1[c] < mp2[c]){ flag = 2; break ; } } if(flag == 0){ printf("Data set #%d: The two strings are the same age\n",cas); } else if(flag == 1){ printf("Data set #%d: First string is older\n",cas); } else{ printf("Data set #%d: First string is younger\n",cas); } if(cas != n){ printf("\n"); } } return 0; }
C. Clean Up the Powers that Be:
solution:
涉及知识点:结构体,排序,模拟
这道题有一个坑人的地方,就是指数和幂数如果等于0,要记一下长度为1,而不是0
std:
#include <bits/stdc++.h> using namespace std; #define ll long long const int maxn = 1005; int pre1[maxn],pre2[maxn]; struct node{ int x,y; }; node a[maxn]; bool cmp(node p1,node p2) { if(p1.x == p2.x) return p1.y < p2.y; return p1.x < p2.x; } map<int , int> mp; int main() { int t; cin>>t; for(int cas = 1;cas <= t; cas++) { mp.clear(); int n; cin>>n; int cnt = 0,x,y; for(int i=1;i<=n;i++){ cin>>x>>y; if(mp[x]){ a[mp[x]].y += y; }else{ a[++cnt].x = x; a[cnt].y = y; mp[x] = cnt; } } sort(a+1,a+1+cnt,cmp); for(int i=1;i<=cnt;i++){ int x = a[i].x; int y = a[i].y; int cnt1 = 0,cnt2 = 0; if(x == 0) cnt1 = 1; if(y == 0) cnt2 = 1; while(x){ x /= 10,cnt1++; } while(y){ y/=10,cnt2++; } pre1[i] = cnt1; pre2[i] = cnt2; } printf("Prime Factorization #%d:\n",cas); for(int i=1;i<=cnt;i++){ for(int j=1;j<=pre1[i];j++){ printf(" "); } printf("%d",a[i].y); } printf("\n"); for(int i=1;i<=cnt;i++){ for(int j=1;j<=pre2[i-1];j++){ printf(" "); } printf("%d",a[i].x); } for(int j=1;j<=pre2[cnt];j++){ printf(" "); } printf("\n"); if(cas != t){ printf("\n"); } } return 0; }
D. The Clock Algorithm:
solution:
涉及知识点:读题!
这道题我在浩洋哥哥的帮助下,一个小时才读明白,整理成了能看懂的样子
这是样例2的解释,希望能帮到大家:
std:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll maxn = 1e5 + 5; struct node{ ll x; ll flag;///1为新,0为旧 }N[maxn]; ll a[maxn]; int main(){ ll n, m, Case = 1; while(~scanf("%lld%lld",&n,&m)){ if(!n && !m) break; memset(a, 0, sizeof(a)); ll Count = 1, sum = 0; ///指针,计数 for(ll i = 1; i <= m; i++) scanf("%lld", &a[i]); for(ll i = 1; i <= n; i++){ N[i].x = 0; N[i].flag = 0; } printf("Program %lld\n", Case++); for(ll i = 1; i <= m; i++){ ll flag1 = 0; for(ll j = 1; j <= n; j++){ if(N[j].x == a[i]){ N[j].flag = 1; flag1 = 1; printf("Access page %lld in cell %lld.\n", a[i], j); sum++; } } if(flag1 == 1) continue; while(N[Count].flag == 1){ N[Count].flag = 0; Count++; if(Count == n + 1){ Count = 1; } } if(N[Count].flag == 0){ N[Count].x = a[i]; N[Count].flag = 1; printf("Page %lld loaded into cell %lld.\n", a[i], Count); Count++; if(Count == n + 1) Count = 1; } } printf("There are a total of %lld page faults.\n\n", m - sum); } return 0; }
F. Metallic Equipment Rigid:
solution:
涉及知识点:计算几何,线段和圆的相交判断
比较简单的一道计算几何板子题吧,起码我这个计算几何没做几道的还会做
先枚举每个点是否在某个圆内,然后将n个点按照先后顺序组成n-1条线,判断是否和m个圆相交
std:
#include <bits/stdc++.h> using namespace std; #define ll long long const int maxn = 55; struct point { double x; double y; }; point A[maxn],O[maxn]; int pan_duan(point p1,point p2,double r,int k) { double a,b,c,dist1,dist2,angle1,angle2;//ax+by+c=0; if(p1.x==p2.x) a=1,b=0,c=-p1.x;//特殊情况判断,分母不能为零 else if(p1.y==p2.y) a=0,b=1,c=-p1.y;//特殊情况判断,分母不能为零 else { a=p1.y - p2.y; b=p2.x - p1.x; c=p1.x*p2.y - p1.y*p2.x; } dist1=a*O[k].x+b*O[k].y+c; dist1*=dist1; dist2=(a*a+b*b)*r*r; if(dist1 > dist2) return 0;//点到直线距离大于半径 angle1=(O[k].x-p1.x)*(p2.x-p1.x)+(O[k].y-p1.y)*(p2.y-p1.y); angle2=(O[k].x-p2.x)*(p1.x-p2.x)+(O[k].y-p2.y)*(p1.y-p2.y); if(angle1>0&&angle2>0)return 1; return 0; } double r[maxn]; int ans[maxn]; int main() { int t; cin>>t; for(int cas = 1;cas <= t;cas++) { int n,m; cin>>n>>m; for(int i=1;i<=n;i++){ ans[i] = 0; scanf("%lf%lf%lf",&O[i].x,&O[i].y,&r[i]); } for(int i=1;i<=m;i++){ scanf("%lf%lf",&A[i].x,&A[i].y); } int flag = 0; for(int j=1;j<=m;j++){ double x0 = A[j].x,y0 = A[j].y; for(int i=1;i<=n;i++){ double cnt1 = fabs((O[i].x - x0)*(O[i].x - x0)); double cnt2 = fabs((O[i].y - y0)*(O[i].y - y0)); if(cnt1 + cnt2 <= r[i]*r[i]){ ans[i] = 1; flag = 1; } } } for(int i=2;i<=m;i++){ for(int j=1;j<=n;j++){ if(pan_duan( A[i] , A[i-1] , r[j] , j) == 1){ ans[j] = 1; flag = 1; //cout<<i<<endl; } } } printf("Compound #%d: ",cas); if(flag == 0){ printf("Rigid Reptile was undetected\n"); } else{ printf("Reptile triggered these cameras:"); for(int i=1;i<=n;i++){ if(ans[i] == 1) printf(" %d",i); }printf("\n"); } if(cas != t) printf("\n"); } return 0; }
G:
solution:
涉及知识点:题解给的是括号匹配模拟,我写了个简单搜索,反正都对
我这里讲一下搜索吧,因为要求字符串要么是s,要么是t,搜索过程中判断,如果是s,那么可以有3种情况,如果是t,那么可以有两种情况,否则无解,按照要求写应该没什么问题吧,模拟呗
std:
#include <bits/stdc++.h> using namespace std; #define ll long long const int maxn = 55; int dfs(string s,int k) { if(k == 1){ if(s.length() == 0){ return 1; } if(s[0] == 'c'){ string ss = s.substr(1,s.length()-1); return dfs(ss , 1); } else if(s[0] == 'a'){ int len = s.length(); for(int i=2;i<len;i++){ if(s[i] == 'b'){ string ss = s.substr(1 , i-1); string sss = s.substr(i+1,len - i - 1); //cout<<ss<<" "<<sss<<endl; if(dfs(ss , 0)&&dfs(sss,1) == 1){ return 1; } } } return 0; } else{ return 0; } }else{ if(s.length() == 0){ return 0; } if(s[0] == 'c'){ string ss = s.substr(1,s.length()-1); return dfs(ss , 1); } else if(s[0] == 'a'){ int len = s.length(); for(int i=2;i<len;i++){ if(s[i] == 'b'){ string ss = s.substr(1 , i-1); string sss = s.substr(i+1,len - i - 1); if(dfs(ss , 0)&&dfs(sss,1) == 1){ return 1; } } } return 0; } else{ return 0; } } } string s; int main() { int t; cin>>t; for(int cas = 1;cas <= t;cas++) { cin>>s; printf("Pattern %d: ",cas); int x = dfs(s , 1); int y = dfs(s , 0); if(x || y){ printf("More aliens!\n"); }else{ printf("Still Looking.\n"); } if(cas != t){ printf("\n"); } } return 0; }
H:
solution:
涉及知识点:排序
签到题,排序输出
std:
#include <bits/stdc++.h> using namespace std; #define ll long long int a[3],b[3]; int main() { int n; cin>>n; for(int cas = 1;cas <= n;cas++) { for(int i=0;i<3;i++){ cin>>a[i]; b[i] = a[i]; } sort(a,a+3); printf("Data set #%d:\n",cas); printf(" Original order:"); for(int i=0;i<3;i++) printf(" %d",b[i]); printf("\n"); printf(" Smallest to largest:"); for(int i=0;i<3;i++) printf(" %d",a[i]); printf("\n"); if(cas != n) printf("\n"); } return 0; }