ACM预备队-大一下学期week(1)集训
1. 2023/3/15:
1.P8605 [蓝桥杯 2013 国 AC] 网络寻路(同学问的,自己补的)
题目链接:P8605 [蓝桥杯 2013 国 AC] 网络寻路 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
分析:我们的做法是记录所有点的度,然后枚举一个点所有相连接的点,他们之间的合法路径的总条数为:
(d[i]-1)*(d[j]-1),减1是因为减掉了两个点之间相连的边。
1 #include <bits/stdc++.h>
2 using namespace std;
3 const int N=1e4+10;
4 const int M=2e5+10;
5 int e[M],ne[M],h[N],idx;
6 int n,m,d[N],ans;
7 void add(int a,int b)
8 {
9 e[idx]=b,ne[idx]=h[a],h[a]=idx++;
10 }
11 int dfs(int u)
12 {
13 int res=0;
14 for(int i=h[u];i!=-1;i=ne[i])
15 {
16 int j=e[i];
17 if(j>u)//防止重复来回搜索,必须要确定一个大小关系
18 {
19 res+=(d[u]-1)*(d[j]-1);
20 }
21 }
22 return res;
23 }
24 signed main()
25 {
26 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
27 memset(h,-1,sizeof h);
28 cin>>n>>m;
29 int a,b;
30 for(int i=0;i<m;i++)
31 {
32 cin>>a>>b;
33 d[a]++,d[b]++;
34 add(a,b),add(b,a);
35 }
36 for(int i=1;i<=n;i++)
37 {
38 ans+=dfs(i);
39 }
40 ans=ans*2;
41 cout<<ans<<endl;
42 return 0;
43 }
2.特殊的正方形(模拟)
可以记录一下该点是第几层,判断第几层的方法就是选取这个点距离上下左右水平距离,注意因为此时坐标是(1,1)开始,所以此点行列都对,但是补行和补列都得+1
比如2*2的矩阵,第(1,2)这个点,本质上从右往左数是第一圈,并不是第0圈,所以需要+1。
题目链接:特殊的正方形 - Problem - Daimayuan Online Judge
1 #include <bits/stdc++.h>
2 using namespace std;
3 const int N=101;
4 int n;
5 char p[N][N];
6 int main()
7 {
8 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
9 cin>>n;
10 for(int i=1;i<=n;i++)
11 for(int j=1;j<=n;j++)
12 {
13 int a=min(i,n-i+1);
14 int b=min(j,n-j+1);
15 int res=min(a,b);
16 if(res%2==1) p[i][j]='+';
17 else p[i][j]='.';
18 }
19
20 for(int i=1;i<=n;i++)
21 {
22 for(int j=1;j<=n;j++)
23 cout<<p[i][j];
24 if(i!=n)cout<<endl;
25 }
26 return 0;
27 }
3.走楼梯2(DP)
题目链接:走楼梯2 - Problem - Daimayuan Online Judge
开LL!!!
动态规划,思路是斐波那契数列,只是多加了限定条件,不能连续走3步
f[i][j]可能是由前一级台阶走过来的,也可能由前两级台阶走过来的
从前一级台阶走过来: f[i][j]=f[i-1][0]+f[i-1][1]+f[i-1][2],前一级台阶可能已经连续走了0或1或2次两级台阶
从前两级台阶走过来: f[i][j]=f[i-2][0]+f[i-2][1],前两级台阶可能已经连续走了0或1次两级台阶
接下来初始化:
f[0][0]=1:第0级台阶,已经走了0次两级台阶,为一种方案
1 #include <bits/stdc++.h>
2 using namespace std;
3 const int N=55;
4 typedef long long LL;
5 LL f[N][N];//f[i][j]表示此时走到了第i台阶,已经连续走了j次跨2阶,由此可见,j=0,1,2;
6 int n;
7 signed main()
8 {
9 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
10 cin>>n;
11 f[0][0]=1;
12 for(int i=1;i<=n;i++)
13 {
14 f[i][0]=f[i-1][0]+f[i-1][1]+f[i-1][2];//状态转移跨一步的
15 if(i<2)continue;
16 else//跨两步的
17 {
18 f[i][1]=f[i-2][0];
19 f[i][2]=f[i-2][1];
20 }
21 }
22 cout<<f[n][0]+f[n][1]+f[n][2];
23 return 0;
24 }
补:摆了好长时间。。。还好重新回正轨了qwq,继续努力!
2. 2023/3/16:
1.走路
题目链接:走路 - Problem - Daimayuan Online Judge
本质上其实是一个动态规划,就看转移方程怎么写
考虑动态规划,状态方程的含义:考虑前i次,能走到第j个地方
假如当前选择走a步,f[i][j]->f[i+1]j+a],f[i][j]能走到的地方f[i+1][j]都能
走到.(g+a≤m)
走b步同理.
1 #include <bits/stdc++.h>
2 #define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
3
4 using namespace std;
5
6 const int N=110;
7 const int M=1e5+10;
8 bool f[N][M];
9 void solve()
10 {
11 int n,m;
12 cin>>n>>m;
13 f[0][0]=true;
14 for(int i=0;i<n;i++)
15 {
16 int a,b;
17 cin>>a>>b;
18 for(int j=0;j<=m;j++)
19 {
20 if(a+j<=m)f[i+1][j+a]|=f[i][j];
21 if(b+j<=m)f[i+1][j+b]|=f[i][j];
22 }
23 }
24 for(int i=0;i<=m;i++)
25 cout<<f[n][i];
26 }
27 signed main()
28 {
29 io;
30 solve();
31 return 0;
32 }
2.简单分数统计
题目链接:简单分数统计 - Problem - Daimayuan Online Judge
这个题没什么好说的,就是纯粹模拟这个考试对照分数的进程即可
1 #include <bits/stdc++.h>
2 #define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
3 using namespace std;
4 const int N=210;
5 string id[N],ques[N];
6 string cp_id[N],cp_ques[N],res[N];
7 int score[N],f_ans[N];
8 int n,m,k;
9 int main()
10 {
11 io;
12 cin>>n>>m>>k;
13 for(int i=0;i<n;i++)
14 cin>>id[i];
15 for(int i=0;i<m;i++)
16 cin>>ques[i]>>score[i];
17 for(int i=0;i<k;i++)
18 {
19 cin>>cp_id[i]>>cp_ques[i]>>res[i];
20 for(int j=0;j<n;j++)//枚举第j个人
21 {
22 if(cp_id[i]==id[j])
23 {
24 for(int q=0;q<m;q++)
25 {
26 if(res[i]=="AC"&&ques[q]==cp_ques[i]) f_ans[j]+=score[q];
27 }
28 }
29 }
30 }
31 for(int i=0;i<n;i++)
32 cout<<id[i]<<" "<<f_ans[i]<<endl;
33
34
35
36 return 0;
37 }
3. 2023/3/17:
1.Alice的德州扑克
题目链接:Alice的德州扑克 - Problem - Daimayuan Online Judge
暴力枚举,但是注意数据是递增的,所以不用自定义类型排序,如果不是递增的,那排序后打表的效率会大大增加
1 #include <bits/stdc++.h>
2 using namespace std;
3 //暴力打表,却依旧蕴含排序后的优化思想,所以只用打部分的表即可
4 struct node{
5 int a;//点数
6 int b;//花色
7 }q[10];
8 inline void solve()
9 {
10 for(int i=1;i<=5;i++)
11 cin>>q[i].a;
12 for(int i=1;i<=5;i++)
13 cin>>q[i].b;
14
15 if(q[1].a+1==q[2].a && q[2].a+1==q[3].a && q[3].a+1==q[4].a && q[4].a+1==q[5].a)//顺子
16 {
17 if(q[1].b==q[2].b && q[2].b==q[3].b && q[3].b==q[4].b && q[4].b==q[5].b)//五张花色相同
18 {
19 if(q[5].a==14)cout<<"ROYAL FLUSH";
20 else cout<<"STRAIGHT FLUSH";
21 }
22 else cout<<"STRAIGHT";
23 }
24 else if(q[1].a==q[2].a && q[2].a==q[3].a && q[3].a==q[4].a || q[2].a==q[3].a && q[3].a==q[4].a && q[4].a==q[5].a)//四张点数相同
25 {
26 cout<<"FOUR OF A KIND";
27 }
28 else if(q[1].a==q[2].a && q[2].a==q[3].a && q[4].a==q[5].a || q[1].a==q[2].a && q[3].a==q[4].a && q[4].a==q[5].a)//三张点数相同,另外两张也相同
29 cout<<"FULL HOUSE" ;
30 else if(q[1].b==q[2].b && q[2].b==q[3].b && q[3].b==q[4].b && q[4].b==q[5].b)
31 cout<<"FLUSH";
32 else cout<<"FOLD";
33 }
34 signed main()
35 {
36 solve();
37 return 0;
38 }
2.订单编号
题目链接:订单编号 - Problem - Daimayuan Online Judge
两种做法,想不到,太笨了qwq
1.利用set数组维护每次插入数据后,只有两种情况,要么这个数据在所维护未被利用的区间的中间,要么这个数据在已知大于这个数字最小的右端点区间的左边界左边,依据右端点排序,观察数据大概率是二分,思路,学到了用set来维护区间
2.利用并查集和unordered_map哈希表来巧妙处理父节点的父节点的父节点的。。。直到祖宗节点,最后把这个祖宗节点不断再+1
法一:
1 //法一:用set二元组来维护还没被处理过的区间
2 #include <bits/stdc++.h>
3 #define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
4 using namespace std;
5 const int N=5e5+10;
6 int n;
7 int a[N];
8 set<pair<int,int> > st;
9 inline void insert(int l,int r)
10 {
11 if(l>r)return;
12 st.insert({r,l});
13 }
14 void solve()
15 {
16 cin>>n;
17 for(int i=0;i<n;i++)
18 cin>>a[i];
19 st.insert({2e9,1});//从右端点开始排序
20 for(int i=0;i<n;i++)
21 {
22 auto it=st.upper_bound({a[i],0});
23 int l=it->second,r=it->first;
24 if(a[i]>l)
25 {
26 cout<<a[i]<<' ';
27 st.erase(it);
28 insert(l,a[i]-1);
29 insert(a[i]+1,r);
30 }
31 else
32 {
33 cout<<l<<' ';
34 st.erase(it);
35 insert(l+1,r);
36 }
37 }
38
39 }
40 signed main()
41 {
42 io;
43 solve();
44 return 0;
45 }
法二:
1 #include <bits/stdc++.h>
2 using namespace std;
3 unordered_map<int,int> m;
4
5 inline int find(int x)
6 {
7 if(!m[x])return x;
8 return m[x]=find(m[x]);
9 }
10 inline void solve()
11 {
12 int n;
13 cin>>n;
14 for(int i=0;i<n;i++)
15 {
16 int x;
17 cin>>x;
18 int t=find(x);
19 cout<<t<<' ';
20 m[t]=t+1;
21 }
22 }
23 signed main()
24 {
25 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
26 solve();
27 return 0;
28 }
4. 2023/3/18:
1.饿饿 饭饭
题目链接:饿饿 饭饭 - Problem - Daimayuan Online Judge
首先观察题目的数据,5e5,最大nlogn,所以考虑二分出最后终止之前的完整轮数,不完整的最后一轮模拟,用deque双端队列一个一个模拟即可
1 #include <bits/stdc++.h>
2 #define int long long
3 using namespace std;
4 const int N=1e5+10;
5 int n,k;
6 int a[N];
7 bool check(int x)
8 {
9 int sum=0;
10 for(int i=1;i<=n;i++)
11 sum+=min(a[i],x);
12 return sum<=k;
13 }
14 inline void solve()
15 {
16 cin>>n>>k;
17 int sum=0;
18 for(int i=1;i<=n;i++)
19 cin>>a[i],sum+=a[i];
20 if(sum<k){cout<<"-1"<<endl;return;}
21 if(sum==k){cout<<endl;return;}
22 //否则实际饭一定还有剩余
23 //二分最大的完整轮数
24 int l=0,r=1e9;
25 while(l<r)
26 {
27 int mid=l + r + 1 >> 1;
28 if(check(mid))l=mid;
29 else r=mid-1;
30 }//l是最大轮数
31 int res=0;
32 for(int i=1;i<=n;i++)
33 res+=min(a[i],l),a[i]-=min(l,a[i]);
34 k-=res;//手动模拟最后不完整的一轮
35 deque<int> q;//双端队列
36 for(int i=1;i<=n;i++)
37 if(a[i])q.push_back(i);
38 while(k--)
39 {
40 a[q.front()]--;
41 if(a[q.front()]>0)q.push_back(q.front());
42 q.pop_front();
43 }
44 for(int x:q)cout<<x<<' ';
45 cout<<endl;
46
47 }
48 signed main()
49 {
50 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
51 solve();
52 return 0;
53 }
2.任务分配
题目链接:任务分配 - Problem - Daimayuan Online Judge
emm,dp,但是至于为什么不用贪心,还需要再悟悟,目前个人理解就是贪心不能保证全局最优解不包含在局部最优解的情况,所以用更加全面的dp来写,看似不一定是最大值或者最小值,但一定是当前子问题的最优解,
注意:每次收益最大的时候即为这个任务做完的时刻(创建一个结构体),所以用右端点排序。
1 //dp 每次收益最大一定是完成某个任务的结束点
2 #include <bits/stdc++.h>
3 using namespace std;
4 const int N=1e3+10;
5 int f[N];//f[i]表示i时刻结束的最大收益
6 int n;
7 struct node{
8 int s;//start开始时间
9 int e;//end结束时间
10 int w;//权重
11 }a[N];
12 bool cmp(node a,node b)
13 {
14 if(a.e=b.e)return a.s<b.s;
15 else return a.e<b.e;
16 }//按照结束时间排序
17 inline void solve()
18 {
19 cin>>n;
20 for(int i=1;i<=n;i++)
21 cin>>a[i].s>>a[i].e>>a[i].w;
22 sort(a+1,a+1+n,cmp);
23 for(int i=1;i<=n;i++)
24 for(int j=0;j<=i;j++)
25 if(a[j].e<=a[i].s)
26 f[a[i].e]=max(f[a[i].e],f[a[j].e]+a[i].w);
27 int ans=0;
28 for(int i=1;i<=n;i++)ans=max(ans,f[a[i].e]);
29 cout<<ans<<endl;
30 }
31 signed main()
32 {
33 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
34 solve();
35 return 0;
36 }
3.路径计数
题目链接:路径计数 - Problem - Daimayuan Online Judge
这个就是最简单的dp,但是捏麻麻的debug了一个小时,原因是:所有前一步都会影响下一步,所以初始化第一行第一列的时候,当前一个点不能走了(0),这一行/这一列之后的点都是0,而不能直接单个判断每个点是否是0来决定其f[i][j]是否是可走的。牵一发而动全身!!!!
1 #include <bits/stdc++.h>
2 using namespace std;
3 #define int long long
4 const int N=1100;
5 const int MOD=1e9+7;
6 int f[N][N],a[N][N];
7 int n;//dp f表示方案数,a表示地图
8 inline void solve()
9 {
10 cin>>n;
11 bool flag=1;//一定要有一个flag,因为一旦第一行或者第一列的某个点是0,则后面的点都是0,即前面都没办法到,必经之路被挡住了
12 //!!!!!!!!!!!!大坑,捏妈妈,我是傻逼,看了一个小时才看出来
13 for(int i=0;i<n;i++)
14 for(int j=0;j<n;j++)
15 cin>>a[i][j];
16
17 for(int i=0;i<n;i++){
18
19 if(a[0][i]==0)flag=false;
20 if(flag) f[0][i]=1;
21 else f[0][i]=0;
22 }
23 flag=true;
24 for(int i=0;i<n;i++){
25
26 if(a[i][0]==0)flag=false;
27 if(flag) f[i][0]=1;
28 else f[i][0]=0;
29 }
30 for(int i=1;i<n;i++)
31 for(int j=1;j<n;j++)
32 if(!a[i][j])f[i][j]=0;
33 else f[i][j]=(f[i-1][j]+f[i][j-1]) % MOD;
34
35 cout<<f[n-1][n-1]<<endl;
36
37
38 }
39 signed main()
40 {
41 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
42 solve();
43 return 0;
44 }
4.最大和上升子序列
题目链接:最大和上升子序列 - Problem - Daimayuan Online Judge
这是一道经典的dp问题
1 #include <bits/stdc++.h>
2 using namespace std;
3 const int N=1010;
4 int n,ans;
5 int a[N],f[N];//dp
6 inline void solve()
7 {
8 cin>>n;
9 for(int i=0;i<n;i++)
10 cin>>a[i];
11
12 for(int i=0;i<n;i++)
13 {
14 f[i]=a[i];//初始化
15 for(int j=0;j<i;j++)
16 {
17 if(a[j]<a[i])f[i]=max(f[i],f[j]+a[i]);
18 }
19 }
20
21 for(int i=0;i<n;i++)
22 ans=max(ans,f[i]);
23
24 cout<<ans<<endl;
25 }
26 signed main()
27 {
28 ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
29 solve();
30 return 0;
31 }
5.加一
题目链接:加一 - Problem - Daimayuan Online Judge
思路很新,也是用dp,f[i] [j]的意思是:原本是j的数位经过i次操作后变成了f[i] [j]位。
注意,这个题卡了读写数据,必须用scanf和printf。ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)都不可以!事实证明还是前者更快一点
1 #include <bits/stdc++.h>
2 using namespace std;
3 const int MOD=1e9+7;
4 typedef long long LL;
5 int n;
6 const int N=2e5+10;
7 LL f[N+50][20];//f[i][j]表示当前数位是j的数字操作i次后,整个数字的位数
8 inline void solve()
9 {
10 scanf("%d",&n);
11 for(int i=0;i<=9;i++)f[0][i]=1;
12 for(int i=1;i<=N;i++)//预处理,类似于前缀和
13 {
14 for(int j=1;j<=9;j++)f[i][j-1]=f[i-1][j];
15 f[i][9]=(f[i-1][0]+f[i-1][1])%MOD;
16 }
17 while(n--)
18 {
19 char str[20];
20 int m=0,res=0;
21 scanf("%s %d",&str,&m);
22 int len=strlen(str);
23 for(int i=0;i<len;i++)
24 {
25 res+=f[m][str[i]-'0'];
26 res%=MOD;
27 }
28 printf("%d\n",res);
29 }
30 }
31 signed main()
32 {
33 solve();
34 return 0;
35 }
本文作者:Zac-saodiseng
本文链接:https://www.cnblogs.com/Zac-saodiseng/p/17220415.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步