ACM预备队-大一下学期week(2)集训

发布时间 2023-03-22 21:15:57作者: Zac-saodiseng

1. 2023/3/20:

1.python3的dfs

 1 n, p = map(int, input().split())
 2 
 3 
 4 def change_to_num(lst):  # 将一个列表转化成一个数字
 5     x = 0
 6     for i in range(len(lst)):
 7         x += lst[i] * 10 ** (len(lst) - 1 - i)
 8     return x
 9 
10 
11 l = []
12 
13 
14 def change_to_lst(num):
15     tmp = []
16     while num > 0:
17         tmp.append(num % 10)
18         num //= 10
19     return tmp
20 
21 
22 l = change_to_lst(n)
23 
24 l = l[::-1]
25 
26 m = 0
27 
28 
29 def flush_max(lst):
30     global m
31     if change_to_num(lst) % p == 0:
32         if change_to_num(lst) > m:
33             m = change_to_num(lst)
34 
35 
36 def dfs(lst, cnt):  # 传入列表和位数
37     global m
38     if cnt >= len(lst):
39         return m
40     if 0 < lst[cnt] < 9:
41         lst[cnt] += 1  # 加一操作
42         flush_max(lst)
43         dfs(lst, cnt + 1)
44         lst[cnt] -= 1  # 不变操作
45         flush_max(lst)
46         dfs(lst, cnt + 1)
47         lst[cnt] -= 1  # 减一操作
48         flush_max(lst)
49         dfs(lst, cnt + 1)
50     elif lst[cnt] == 0:
51         flush_max(lst)
52         dfs(lst, cnt + 1)
53         lst[cnt] += 1
54         flush_max(lst)
55         dfs(lst, cnt + 1)
56     else:
57         flush_max(lst)
58         dfs(lst, cnt + 1)
59         lst[cnt] -= 1
60         flush_max(lst)
61         dfs(lst, cnt + 1)
62     return m  # 每一个递归搜索树都有一个返回值
63 
64 
65 if (dfs(l, 0)) == 0:
66     print(-1)
67 else:
68     print(dfs(l, 0))

 2.跳跳

题目链接:跳跳 - Problem - Daimayuan Online Judge

如果需要掌握最少的魔法阵方法数目,每次移动的距离就得最少,这样就可以遍历地图上最多的点,所以每次移动的距离就是x1-x2 和 y1-y2 分别除以他俩的最大公因数,这样能保证每一步走的路程是最小的。set可以保证去重。

但是要注意的是,两个负数的公因数也是负数,所以负数除以负数依旧是正数,所以最后还需要用set去重后的size()*2才是最后走的掌握最少的魔法阵数量

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N=510;
 4 typedef pair<int,int> PII;
 5 set <PII> s;
 6 struct point{
 7     int x;
 8     int y;
 9 }p[N];
10 int gcd(int a,int b)
11 {
12     if(b==0)return a;
13     return gcd(b,a%b);
14 }
15 inline void solve()
16 {
17     int n;
18     cin>>n;
19     for(int i=1;i<=n;i++)
20         cin>>p[i].x>>p[i].y;
21     for(int i=1;i<=n;i++)
22         for(int j=1;j<=n;j++)
23         {
24             if(i==j)continue;
25             int dx=p[i].x-p[j].x;
26             int dy=p[i].y-p[j].y;
27             if(dx==0 && dy!=0)s.insert({0,1});
28             else if(dx!=0 && dy==0)s.insert({1,0});
29             else{
30                 int x=gcd(dx,dy);
31                 s.insert({dx/x,dy/x});
32             }
33         }
34     cout<<s.size()*2<<endl;
35 }
36 signed main()
37 {
38     ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
39     solve();
40     return 0;
41 }

3.异或和或

题目链接:异或和或 - Problem - Daimayuan Online Judge

 

 

 此题打表找找规律即可,会发现str1和str2在长度相同的情况下,全为‘0’,或者都不全为‘0’就输出YES,时间复杂度O(n^2)

这个题更像是一个脑筋急转弯,长度不相等,一定是NO

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int t;
 4 inline void solve()
 5 {
 6     cin>>t;
 7     while(t--)
 8     {
 9         string s,t;
10         cin>>s>>t;
11         if(s.length()!=t.length())
12         {
13             cout<<"NO"<<endl;
14             continue;
15         }
16         bool f_s=false;
17         for(int i=0;i<s.length();i++)
18             if(s[i]=='1')
19             {
20                 f_s=true;
21                 break;
22             }
23         bool f_t=false;
24         for(int j=0;j<s.length();j++)
25             if(t[j]=='1')
26             {
27                 f_t=true;
28                 break;
29             }
30         if(f_t == f_s)cout<<"YES"<<endl;
31         else cout<<"NO"<<endl;
32     }
33 }
34 signed main()
35 {
36     ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
37     solve();
38     return 0;
39 } 

 

2. 2023/3/21:

1.01序列

题目链接:01序列 - Problem - Daimayuan Online Judge

我们当然可以暴力的遍历字串,但是需要O(n^2)的时间复杂度,肯定会超时,所以此处运用了类似于前缀和的思想,因为数列只有01,所以其实是求区间和是k的序列的总数量。

 

 

 f[i]表示1~i数位的区间和,类似于前缀和             cnt[i]表示区间和为i的总数量

注意要初始化cnt[0]=1,要不区间和恰好等于前缀和的情况算不进去。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const int N=1e6+10;
 5 LL cnt[N],f[N];//cnt[i]表示区间和为i的数量有多少 ,f[i]表示第一位到第i位总长度的区间和 
 6 LL res;
 7 inline void solve()
 8 {
 9     int k;
10     string str;
11     cin>>k>>str;
12     str="$"+str;
13     for(int i=1;i<str.size();i++)
14         f[i]=f[i-1]+(str[i]-'0');
15     cnt[0]=1;
16     for(int i=1;i<str.size();i++)
17     {
18         if(f[i]>=k) res+=cnt[f[i]-k];
19         cnt[f[i]]++;
20     }
21     cout<< res <<endl;
22 }
23 signed  main()
24 {
25     ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
26     solve();
27     return 0;
28 }

2.出栈序列判断

题目链接:出栈序列判断 - Problem - Daimayuan Online Judge

简单的模拟,用栈

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N=1e5+10;
 4 int n;
 5 int a[N];
 6 stack <int> st;
 7 inline void solve()
 8 {
 9     scanf("%d",&n);
10     for(int i=1;i<=n;i++)
11         scanf("%d",&a[i]);
12     int cnt=1;
13     for(int i=1;i<=n;i++)
14     {
15         st.push(i);
16         printf("push %d\n",i);
17         while(!st.empty() && st.top()==a[cnt])//一定要先判断栈是否为空,否则如果空栈并且a[cnt]=null,会发生错误
18         {
19             st.pop();
20             cnt++;
21             printf("pop\n");
22         }
23     }
24     while(!st.empty())
25     {
26         st.pop();
27         printf("pop\n");
28     }
29     
30 }
31 signed main()
32 {
33     solve();
34     return 0;
35 } 

3.序列维护

emm,本来想用数组模拟,但是数组模拟的例如添加操作不是添加的插入第k个数后面的数,而是插入的是下标是k的数后面的数,其实有一定的歧义

所以果断用vector,其中insert和erase可以实现插入和删除,然后询问直接输出下标即可,注意不要越界

 1 #include <bits/stdc++.h>
 2 #define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
 3 //STL vector支持在中间插入的操作 
 4 using namespace std;
 5 int m;
 6 vector<int> v;
 7 inline void solve()
 8 {
 9     cin>>m;
10     int x,y;
11     while(m--)
12     {
13         string s;
14         cin>>s;//呃,刚开始把s输入放外面去了,我是dinner 
15         if(s=="insert")
16         {
17             cin>>x>>y;
18             v.insert(v.begin()+x,y);
19         }
20         else if(s=="delete")
21         {
22             cin>>x;
23             v.erase(v.begin()+x-1);
24         }
25         else//询问操作 
26         {
27             cin>>x;
28             cout<<v[x-1]<<endl;
29         }
30     }
31 }
32 signed main()
33 {
34     io;
35     solve();
36     return 0;
37 } 

4.网格判断

题目链接:网格判断 - Problem - Daimayuan Online Judge

emm,签到题

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N=30;
 4 char p[N][N];
 5 int n;
 6 signed main()
 7 {
 8     ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
 9     //solve();
10     cin>>n;
11     for(int i=0;i<n;i++)
12         for(int j=0;j<n;j++)
13             cin>>p[i][j];
14     for(int i=0;i<n;i++)//判断每一行是否黑白块数量相同 
15     {
16         int a=0,b=0;
17         for(int j=0;j<n;j++)
18         {
19             if(p[i][j]=='W')a++;
20             else b++;
21         }
22         if(a!=b)
23         {
24             cout<< 0 <<endl;
25             return 0; 
26         }
27     }
28     for(int i=0;i<n;i++)//判断每一列黑白块数量是否相同 
29     {
30         int a=0,b=0;
31         for(int j=0;j<n;j++)
32         {
33             if(p[j][i]=='W')a++;
34             else b++;
35         }
36         if(a!=b)
37         {
38             cout<< 0 <<endl;
39             return 0;
40         }
41     }
42     for(int i=0;i<n;i++)//判断是否每行有三个连续相同颜色的 
43     {
44         for(int j=1;j<n-1;j++)
45         {
46             if(p[i][j]==p[i][j-1] && p[i][j]==p[i][j+1])
47             {
48                 cout<< 0 <<endl;
49                 return 0;
50             }
51         }
52     }
53     for(int i=0;i<n;i++)//判断是否每列有三个连续相同颜色的 
54     {
55         for(int j=1;j<n-1;j++)
56         {
57             if(p[j][i]==p[j-1][i] && p[j][i]==p[j+1][i])
58             {
59                 cout<< 0 <<endl;
60                 return 0;
61             }
62         }
63     }
64     
65     cout<< 1 <<endl;
66     return 0; 
67 } 

 

3. 2023/3/22:

1.删删

题目链接:删删 - Problem - Daimayuan Online Judge

由于鄙人没看到必须删除相同相同相同的字符!!!!!捏麻麻的

暴力做法,枚举26个字符,在其中利用双指针,分别左右扫过来,如果左右指针所指字符都不一样,并且无法这俩字符都不是我要枚举删的字符(删不掉),那么一定不回文!!可以接着枚举下一个了

参考:

枚举26个字符,假设这个字符就是这个字符串要删除的字符,然后双指针跑字符串,当两边不一样时就判断两边有没有那边是我们当前枚举的字符,如果有就跳过他,操作数++然后继续双指针跑字符串,如果两边都没有我们当前枚举的字符,说明这个字符不能把字符串变成回文,我们去枚举下一个字符,如果有字符可以把字符串变成回文,那就把操作数记录下来,并维护最小值。最后如果所有字符都不能把字符串变成回文那就输出-1,反之输出最小的操作数。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int T;
 4 inline void solve()
 5 {
 6     int n;
 7     string s;
 8     cin>>n>>s;
 9     int res=n+1; //一直维护更新即可
10     for(int i=0;i<26;i++)
11     {
12         int l=0,r=n-1,cnt=0;//cnt记录要删除字符的数量
13         char c='a'+i;
14         for(int i=0;i<n;i++)
15         {
16             while(l<=r)
17             {
18                 if(s[l]==s[r])l++,r--;
19                 else if(s[l]==c)l++,cnt++;
20                 else if(s[r]==c)r--,cnt++;
21                 else 
22                 {
23                     cnt=n+1;
24                     break;
25                 }
26             } 
27             res=min(res,cnt);
28         }
29     }
30     if(res==n+1)cout<< -1 <<endl;
31     else cout<< res <<endl;
32 }
33 signed main()
34 {
35     ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
36     cin>>T;
37     while(T--) solve();
38     return 0;
39 }

2.快快变大

题目链接:快快变大 - Problem - Daimayuan Online Judge

区间dp,就是合并石子这道题,dp一般是先枚举区间,然后枚举起点,终点等于起点+长度-1,第三维枚举分边界线k,把整个区间分为[i,k]和[k+1,j],所以k最大的范围就是j-1,因为至少要保证两个区间才可以合并。

这道题可以提前预处理下区间i~j的阶乘

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 //区间dp
 5 const int N=1010;
 6 const int MOD=1000003;
 7 LL f[N][N],dp[N][N],a[N];//f[i][j]表示i到j的阶乘,dp[i][j]表示i到j的最大分数  
 8 inline void solve()
 9 {
10     int n;
11     cin>>n;
12     for(int i=1;i<=n;i++) cin>>a[i];
13     for(int i=1;i<=n;i++)//dp预处理阶乘 
14     {
15         f[i][i-1]=1;
16         for(int j=i;j<=n;j++)
17         {
18             f[i][j]=(f[i][j-1]*a[j])%MOD;
19         }
20     }
21     for(int len=2;len<=n;len++)//长度是1就不用合并了
22     {
23         for(int i=1;i+len-1<=n;i++)
24         {
25             int j=i+len-1;
26             for(int k=i;k<j;k++)//把区间分成i~k和k+1~j 因为最少要分成两个区间,所以上界是j-1 
27             {
28                 dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j] + (f[i][k]-f[k+1][j])* (f[i][k] - f[k + 1][j]));
29             }
30         }
31     }
32     cout<<dp[1][n]<<endl;
33 }
34 signed main()
35 {
36     ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
37     solve();
38     return 0; 
39 }