2019牛客多校训练营第二场补题

https://ac.nowcoder.com/acm/contest/882/F

F题 Partition Problem

题意:有两个队,n 个人,每个人和其他 n-1 有个竞技值,当处于不同队时,就能增加这个值,求最大的总竞技值。

分析:因为 N 最大只有14,所以可以暴力DFS回溯搞组合数。

对于这个问题,就得先构造一个初始状态入手,初始状态也就是全都在B队,由于关键在于对立,所以可以让1先在A队来简化,接下来的入A队就加上该加的竞技值,减去该减的竞技值。

然后每个都用 for 不断往下搜索,记录vis[ ] 和 cnt,如果cnt 满了就比较结果,并退一步把当前位扔掉,然后继续往下搜索(八皇后)。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 30;
int val[maxn][maxn];
bool in[maxn];
int n,m;
ll ans=0;

void dfs(int now,int cnt,ll sum){
    if(cnt==n){
        ans = max(ans,sum);
        return;
    }
    for(int i=now; i<=n+cnt+1; i++){    //这里写n+cnt+1比写m快
        if(!in[i]){
            ll tmp=sum;
            for(int j=1; j<=m; j++){
                if(in[j])
                    sum -= val[i][j];   //这里是i 而我一开始错写成了+=val[now][j],WA了好久
                else
                    sum += val[i][j];
            }
            in[i] = true; 
            dfs(i+1,cnt+1,sum);
            in[i] = false;        //回溯
            sum = tmp;
        }
    }
}

inline int read(){
    int res = 0, w = 0; char ch = 0;
    while(!isdigit(ch)){
        w |= ch == '-', ch = getchar();
    }
    while(isdigit(ch)){
        res = (res << 3) + (res << 1) + (ch ^ 48);
        ch = getchar();
    }
    return w ? -res : res;
}

int main(){
    scanf("%d",&n);
    m = n<<1;
    for(int i=1; i<=m; i++){
        for(int j=1; j<=m; j++){
            val[i][j] = read();
        }
    }
    ll sum;
    sum = ans = 0;
    in[1] = true;   //先构造个初始状态,所有人都在红队,再把1放进白队
    for(int i=2; i<=m; i++){
        sum += val[1][i];
    }
    dfs(2,1,sum);
    printf("%lld\n", ans);
    return 0;
}
View Code

 

https://ac.nowcoder.com/acm/contest/882/H

H题 Second Large Ractangle

悬线法求极大子矩阵的裸题。 相关资料:https://www.cnblogs.com/-Zzz-/p/11503639.html给由01组成的矩阵,问只包含1的子矩阵第二大的面积是多少。

由于这个预处理方法会导致一整块的L,R,U的值都同化,所以还要处理出这一块的(L-1)*H 和 L*(H-1)。

还要注意 g[i][j] 等于 0 时不算数,不要拿去算面积

更新ans1时要用大于等于号

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e3+300;
int g[maxn][maxn],Left[maxn][maxn],Right[maxn][maxn],Up[maxn][maxn];
char ch[maxn];

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++){
        scanf("%s",ch+1);
        for(int j=1; j<=m; j++){
            g[i][j] = ch[j]-'0';
            Up[i][j] = 1;
            Left[i][j] = Right[i][j] = j;
        }
    }
    for(int i=1; i<=n; i++){
        for(int j=2; j<=m; j++){
            if(g[i][j]==1 && g[i][j-1]==1){
                Left[i][j] = Left[i][j-1];
            }
        }
    }
    for(int i=1; i<=n; i++){
        for(int j=m-1; j>=1; j--){
            if(g[i][j]==1 && g[i][j+1]==1){
                Right[i][j] = Right[i][j+1];
            }
        }
    }
    int maxh = 0,maxl = 0;
    int L,R,U,ans1=0,ans2=0;
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(i>1 && g[i][j]==1 && g[i-1][j]==1){
                Up[i][j] = Up[i-1][j]+1;
                Left[i][j] = max(Left[i-1][j], Left[i][j]);
                Right[i][j] = min(Right[i-1][j], Right[i][j]);  //悬线法
            }
            if(ans1<= (Right[i][j] - Left[i][j] + 1)*Up[i][j] ){
                R = Right[i][j], L = Left[i][j], U = Up[i][j];
                maxl = Right[i][j] - Left[i][j] + 1;
                // printf("l=%d\n", maxl);
                maxh = Up[i][j];
                // printf("h=%d\n", maxh);
                ans1 = maxl*maxh;

            }
        }
    }
    ans2 = max(ans2,(maxl-1)*maxh);
    ans2 = max(ans2,maxl*(maxh-1));

    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(g[i][j]==0) continue;
            if(Right[i][j] == R && Left[i][j]==L && Up[i][j]==U) continue;
            ans2 = max( ans2,(Right[i][j] - Left[i][j] + 1)*Up[i][j] );
        }
    }
    printf("%d\n", ans2);
}


// #include <iostream>
// #include <algorithm>
// using namespace std;
 
// const int Max = 2005;
 
// int ves[Max][Max], up[Max][Max], Left[Max][Max], Right[Max][Max];
// int temp1 = 1, temp2 = 1;
 
// int main(void)
// {
//     ios::sync_with_stdio(false);
//     int N, M;
//     cin >> N >> M;
//     for(int i = 1; i <= N; i++)
//         for (int j = 1; j <= M; j++) {
//             cin >> ves[i][j];
//             Left[i][j] = Right[i][j] = j;    //初始化Right和Left,使他们值为点所在纵坐标
//             up[i][j] = 1;    //初始化up使其值为1
//         }
 
//     for (int i = 1; i <= N; i++)
//         for (int j = 2; j <= M; j++)
//             if (ves[i][j] == 1 - ves[i][j - 1])    //判断相邻两个数是否不同
//                 Left[i][j] = Left[i][j - 1];    //是,则
 
//     for (int i = 1; i <= N; i++)
//         for (int j = M - 1; j > 0; j--)
//             if (ves[i][j] == 1 - ves[i][j + 1])
//                 Right[i][j] = Right[i][j + 1];
 
//     for(int i = 1;i <= N; i++)
//         for (int j = 1; j <= M; j++) {
//             if (i > 1 && ves[i][j] == 1 - ves[i - 1][j]) {    //递推公式
//                 Left[i][j] = max(Left[i][j], Left[i - 1][j]);
//                 Right[i][j] = min(Right[i][j], Right[i - 1][j]);
//                 up[i][j] = up[i - 1][j] + 1;
//             }
 
//             int A_instance = Right[i][j] - Left[i][j] + 1;    //计算长度
//             int B_instance = min(A_instance, up[i][j]);    //算出长宽中较小的边,以计算正方形
//             temp1 = max(temp1, B_instance * B_instance);    //正方形面积
//             temp2 = max(temp2, A_instance * up[i][j]);        //长方形面积
//         }
 
//     cout << temp1 << endl << temp2 << endl;
// }
View Code

 

A题 Eddy Walker 随机程序找规律和逆元

参考自https://blog.csdn.net/WHY995987477/article/details/97494292

题意:有n个点的环,初始位置在0,可以随机向前走或向后走,问n个位置都走过,并且最后停在m的概率,最后输出前i种情况的概率。

分析:随机数暴力打表找规律,会发现概率是一样的,都等于1/(n - 1)。以及用到求单个逆元, a^(p2) 就是 a 在 mod p 意义下的逆元

打表的时候n的值不要输入太大!

要分组输入,不要多组输入

另外有两个特殊情况:

1、人一开始站在0上,0已经视为走过了。所以当n == 1 && m == 0 时,开始即终止,概率为1。

2、而当n > 1 && m == 0时,因为走完n个点就结束了,从0点开始走,不可能刚好走完n个点后停在0处,此时的概率是0。

最后要求输出的是每次的结果(概率)的累乘,具体看代码里面的ans。

这里有一个不是用打表找规律做出来的博客:
https://blog.nowcoder.net/n/95f1fe2b38aa4befaba3750128a12786
随机数打表代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int vis[maxn], ans[maxn];
int n;//一共有n个点
int pos;//当前所在位置
int cnt;//已经经过了几个点。
int main()
{
    scanf("%d", &n);//不要用多组样例输入,不要输入太大的n值
    srand((unsigned int)(time(NULL)));
    for(int i = 1; i <= 100000; i++)
    {
        memset(vis, 0, sizeof(vis));
        vis[0] = 1;
        pos = 0;
        cnt = 1;
        while(cnt < n)
        {
            int moving = rand() % 2 ? 1 : -1;
            pos = (pos + moving + n) % n;
            if(!vis[pos])
            {
                vis[pos] = 1;
                cnt++;
            }
            if(cnt == n)
            {
                ans[pos]++;
            }
        }
    }
    for(int i = 0;i < n; i++)
        printf("%d ", ans[i]);
    printf("\n");
    return 0;
}
View Code

AC代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mod = 1e9+7;

ll qpow(int x,int y){
    ll res = 1;
    while(y){
        if(y&1) res= res%mod *x%mod;
        y>>=1;
        x = (ll)x%mod*(ll)x%mod;
    }
    return res;
}

int main(){
    int T,n,m;
    scanf("%d",&T);
    ll res,ans=1;
    while(T--){
        scanf("%d%d",&n,&m);
        if(n==1 && m==0) res=1;
        else if(m==0) res=0;
        else{
            res = 1*qpow(n-1,mod-2);
        }
        ans = ans%mod * res%mod;
        printf("%lld\n", ans);
    }
}
View Code

 

D题 Kth Minimum Clique 优先队列优化的bfs 

参考自:https://www.cnblogs.com/Chen-Jr/p/11222852.html#autoid-1-0-0

题意:让你求出一张图的第k小团的权值。

分析:

 

注意,要防止重复,就是每次找最后一位的下一位开始枚举。

 

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 102;
bitset<maxn> a[maxn];
struct node{
    ll a;
    bitset<maxn> b;
    bool operator < (const node & t)const{
        return a > t.a;
    }
};
priority_queue<node> que;
int w[maxn];
char ch[maxn];

int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=0; i<n; i++){
        scanf("%d", w+i);
    }
    for(int i=0; i<n; i++){
        scanf("%s",ch);
        for(int j=0; j<n; j++){
            if(ch[j] == '1'){
                a[i].set(j);
            }
        }
    }
    bitset<maxn> t; t.reset();
    que.push( (node){0,t} );
    while(!que.empty()){
        node u = que.top();
        bitset<maxn> tmp = u.b;
        que.pop();
        k--;
        if(k==0){
            printf("%lld\n", u.a);
            return 0;
        }
        int pos = 0;
        for(int i=0; i<n; i++){
            if(tmp[i]) pos = i+1;    ///为了保证不会重复加点,我们每次从一个已经加入的点的后一个结点开始枚举新加入的结点
        }    
        for(int i=pos; i<n; i++){
            if( !tmp[i] && (tmp&a[i])==tmp){
                tmp[i] = 1;
                que.push( (node){u.a + w[i] ,tmp} );
                tmp[i] = 0;   //记得这个小回溯
            }
        }
    }
    puts("-1");

}
View Code

 

 

转载于:https://www.cnblogs.com/-Zzz-/p/11504077.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值