A题:https://ac.nowcoder.com/acm/contest/882/A
题意:就是有一个含有n个节点的环,然后一个人开始在节点0,每次等概率选择向左或者向右走,问走完所有n个节点后的位置为节点m的概率,多组询问,输出前i组询问全部发生的概率
这道题我见到了好几种题解,把网上的题解和出题人爸爸想说的意思都看了一遍,表示感觉都不怎么能懂的样子
(1): 第一种见的解法和出题人说的解法是差不多,我觉得出题人可能更意识流点,感觉他们能显然看出来这是1/(n-1) ,好像虽然这个也能看出来,但是就是不知道怎么说呀
(2):打表流法,遇事不绝先打表,这个是最正确的做法,以后得好好提升这个观念和感觉
挂一个打表链接:https://blog.csdn.net/qq_40791842/article/details/96900344
(3):我看到一个观点是,当n>1的时候,m=0肯定是0的,但是当m为其他点呢?????
有一个想法是这个有A(n-1)! 个排列,按照以 m为结尾的排列数肯定是A(n-2)! 这样的概率是1/(n-1)
这个显然是随机走的,所以每个排列的生成概率应该也是一样的
https://blog.csdn.net/weixin_43702895/article/details/98378134
(4)就是推公式了,但是我们一直没有看懂,先把博客放上来,待会再看把
https://www.cnblogs.com/DeaphetS/p/11222816.html
下面是代码:
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 typedef long long ll; 8 using namespace std; 9 const ll mod=(ll)(1e9+7); 10 int t; 11 ll n,m,ans=1; 12 13 ll fast_pow(ll a,ll b){ 14 ll res=1; 15 while(b){ 16 if(b%2==1) res=(res*a)%mod; 17 a=(a*a)%mod; 18 b=b/2; 19 } 20 return res; 21 } 22 23 int main(){ 24 scanf("%d",&t); 25 while(t--){ 26 scanf("%lld%lld",&n,&m); 27 if(n==1){ 28 ans=ans*1; 29 }else{ 30 if(m==0) ans=0; 31 else{ 32 ans=(ans*fast_pow(n-1,mod-2))%mod; 33 } 34 } 35 printf("%lld\n",ans); 36 } 37 return 0; 38 }
F题:https://ac.nowcoder.com/acm/contest/882/F
题意:给定一个n,把2n个人分为两组,再给定一个2n*2n的方阵,aij表示当第i个人和第j个人不在一个组时将得到的武力值,要求你分配输出最大的武力值之和。
(1): 这个如果做的话会有一个很严重的思维定势,这个思维定势就是我们会以为C(28,14)这个数是很大的
(2): 但是如果我们通过简单计算的话,会发现这个数也不是很大,大概4e7左右,这样的话如果我们加上一个n的话是可以做的
(3): 加上一个n后的复杂度大概是C(28,14)*14 大概在4e8左右,这个范围我们是可以做的
(4): 但是如果我们再加一个n的话 这样是4e9左右,对于这个范围,我们是不可做的,这个得搞清楚
(5): 但是有一个东西就是我们在搜索生成排列的时候,我们可以顺便将这个排列的数弄好,将一些不是的弄出,一些是的弄进来
(6): 这样的好处是我们可以在弄排列的过程就把他弄好,这样很多个前缀可以公用一个,而不是到最后面一起输出
(7): 这个就有点记忆化搜索的意思了
(8): 下面是代码:
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 typedef long long ll; 9 using namespace std; 10 int n; 11 ll maps[30][30],d[30]; 12 vector<int> vec; 13 // 用vector还是挺好的,这样比用数组更好 14 // 虽然慢了点,但是这个如果用vector的话,我们可以发现好写了不少 15 // 而且一般比赛也不卡常数 16 ll ans=0; 17 18 //这样只用两个参数的写法,我觉得比我第一次写的那一堆for要好 19 void dfs(int pos,ll sum){ 20 if(vec.size()==n){ 21 ans=max(ans,sum); 22 return; 23 } 24 if(pos>2*n) return; 25 ll cnt=d[pos]; 26 for(int i=0;i ) 27 cnt-=2*maps[pos][vec[i]]; 28 //每次更新值,这个是确保缓存,从n^2降到O(N)的关键 29 //其实应该不止o(n),但是可以类似看作 30 //我之前一直错误的一点是 每次搜索结束 不是只改变了一个点,而是有可能改变了很多很多点 31 //这个应该是要注意的点???????????? 32 //一定要注意好 33 vec.push_back(pos); 34 dfs(pos+1,sum+cnt); 35 vec.pop_back(); 36 dfs(pos+1,sum); 37 } 38 39 int main(){ 40 scanf("%d",&n); 41 for(int i=1;i<=2*n;i++) 42 for(int j=1;j<=2*n;j++){ 43 scanf("%lld",&maps[i][j]); 44 d[i]+=maps[i][j]; 45 } 46 dfs(1,0); 47 printf("%lld\n",ans); 48 return 0; 49 }
H题:https://ac.nowcoder.com/acm/contest/882/H
题意:给你一个n*m的01矩阵,要你求出这个01矩阵中第二大的全1子矩阵,如果没有第二大的矩形就输出0
(1): 这个题有一个很常见的模型的就是我们对每一行单独维护一个全一子矩阵,这样的话这道题就变成了一道模版题??????
(2): 一个很常见的模型这个可以通过单调栈预处理出来?
(3): 然后就很奇妙了,我们该怎么求出第二大了,他可以从那儿出来
(4): 对单调栈模型进行考虑,这个东西移动的时候保存的是以当前这个点的高度为高向左走的矩形的最大值。这样的话我们对单个最高的矩形保存一个最大的矩形就可
(5): 通过单调栈这样的处理的话,我们存下来的东西就是这个单调栈移动下来的以当前矩阵为高的最大矩形的值,但是这样的话我们对每一行每一列我们都只保存了最大值,而次大值我们都把他给舍弃了,这样是不好的。
(6): 而正是因为我们只保存了最大值,我们忽略了次大值的来源,所以我们这道题才会一直wa。
(7): 下面是代码: