• 9-1拼多多题目,4题AK


    需要笔试辅导的可以私我,ACM金牌退役选手,已拿腾讯和阿里offer,可以视频会议1V1辅导,混个脸熟,不收钱。

    num1

    题意:正方形划分为8个部分。
    思路:直接判断就行。代码写得不好看就不发了。

    num2

    题意:给定01矩阵,有一些连通的1,用0分割,现在你最多可以把其中1个1替换位置,问替换之后的最大连通块是多大。
    思路:为了保证做法不会出错,我们得考虑:

    1. 如果只有一个连通块,那么它就是答案;
    2. 多个连通块(最多为4)是否可以移动内部的某个1来连接;
    3. 多个连通块是否可以通过外部的某个1来连接。

    对于2,3种情况,我们把他统一为,如果多个连通块中间只被一个空格隔开,那么答案就是这几个连通块大小之和。同时如果这几个连通块之外还有连通块,那么答案+1。+1是表示用外部的连通块来“”这个空位,否则表示内部的1换了个位置达到了连接的效果.

    综上,我的代码用并查集实现,这样可以得到每个连通块的编号、大小; 移动1,等效于枚举被填的那个位置,然后看4周会不会贡献,贡献并不是独立的,所以还得用并查集判重;最后判定是否+1;

    ----代码写得比较长,不要嫌弃----

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=410;
    int mp[maxn][maxn],fa[maxn*maxn];  //mp为01矩阵,fa表示点所在的集合
    int id[maxn][maxn],ans,num[maxn*maxn],cnt; //id表示矩阵对应的id,num表示集合的大小。
    int tfa[maxn];
    int dx[4]={1,-1,0,0};  //4个方向
    int dy[4]={0,0,1,-1};
    int find(int x)  //并查集
    {
        if(x==fa[x]) return x;
        return fa[x]=find(fa[x]);
    }
    void Merge(int x1,int y1,int x2,int y2) //合并相邻的1
    {
        if(mp[x1][y1]&&mp[x2][y2]){  //如果都为1,才合并。
            int w=find(id[x1][y1]);  //得到id对应的集合。
            int v=find(id[x2][y2]);
            if(w!=v) {   //集合不同就合并。
                num[v]+=num[w];   //大小合并
                ans=max(ans,num[v]);
                fa[w]=fa[v];  //集合id合并。
                cnt--; // 集合的数量-1
            }
        }
    }
    void add(int i,int j,int &tmp,int &sum) //把(i,j)代表的集合加进去,同时记录已经加进去的集合,用tfa[]表示,这里用来防止重复加。
    {
        int d=find(id[i][j]),F=1;
        for(int i=1;i<=tmp;i++){
            if(tfa[i]==d){  //tfa已经存在,则不再加,那么break
                F=0; break;
            }
        }
        if(!F) return ;  //存在,返回。
        tmp++;   
        sum+=num[d];  
        tfa[tmp]=d;
    }
    int main()
    {
        int N,M;
        scanf("%d%d",&N,&M);
        for(int i=1;i<=N;i++){
            for(int j=1;j<=M;j++){
                scanf("%d",&mp[i][j]);
                id[i][j]=(i-1)*M+j;
                if(mp[i][j]) {
                    num[id[i][j]]=1;   //集合初始化大小为1.
                    ans=1;
                    cnt++;   //连通块个数+1。 (cnt用于后来判定是否可以取连通块之外的+1)
                }
                fa[id[i][j]]=id[i][j]; 
            }
        }
        for(int i=1;i<=N;i++){
            for(int j=1;j<=M;j++){
                if(i>1) Merge(i-1,j,i,j);
                if(j>1) Merge(i,j-1,i,j);
            }
        }
        if(cnt>=2){ //cnt=1的时候没必要合并。
            for(int i=1;i<=N;i++){
                for(int j=1;j<=M;j++){
                    if(mp[i][j]) continue; // 只枚举0的位置,然后拿其他1来‘填’。
                    int tmp=0,sum=0;
                    for(int k=0;k<4;k++){ //枚举上下左右是否可以通过这个0来连接。
                        int nowx=i+dx[k],nowy=j+dy[k];
                        if(nowx>=1&&nowx<=N&&nowy>=1&&nowy<=M){
                           if(mp[nowx][nowy]){
                               add(nowx,nowy,tmp,sum);
                           }
                        }
                    }
                    if(tmp==cnt) ans=max(ans,sum);
                    else ans=max(ans,sum+1);//表示这上下左右之外的连通块可以贡献一个1来填,所以+1;
                }
            }
        }
        printf("%d
    ",ans);
        return 0;
    }

    num3

    题意:普通的01背包,但是现在体积可能为负数。
    思路:一眼题,因为体积为负数,表示我们的体积会变大,这种情况,我们把它变为相反数即可--------表示我一开始就装进去,那么在跑背包的时候如果选择了它的相反数这个物体,代表把它移除。(而这个时候所有的物体都是正他体积,跑01背包就行。)
    注:如果你不会01背包的话,那么你在学习的时候得注意,第二个for倒序遍历,这样防止重复更新答案。或者用二维的dp。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=410;
    int c[maxn],v[maxn],ans;
    int dp[40010];
    int main()
    {
        int N,M;
        scanf("%d%d",&N,&M);
        for(int i=1;i<=N;i++){
            scanf("%d%d",&c[i],&v[i]);
            if(c[i]<=0){   //负的物体取反
                ans+=v[i];
                M-=c[i];
                c[i]=-c[i];
                v[i]=-v[i];
            }
        }
        for(int i=1;i<=N;i++)  //01背包
            for(int j=M;j>=c[i];j--)
                dp[j]=max(dp[j],dp[j-c[i]]+v[i]);
    
        for(int i=0;i<=M;i++) {
                dp[M]=max(dp[M],dp[i]);
        }
        printf("%d
    ",ans+dp[M]);
        return 0;
    }

    num4

    位运算和gcd
    容斥原理,求所有m的集合,在这个集合的所有数,求一下lcm(最小公倍数),如果|集合|是奇数,ans+=n/lcm,否则ans-=n/lcm 。

    具体的:先从简单的问题走起,问1到10里面多少个2的倍数------->     ans=10/2=5;

                                                       问1到10里面多少个5的倍数------->     ans=10/5=2;

                                                       问1到10里面多少个2或者5的倍数------->     ans=10/5+10/2-10/10=6;

    其实也就是容斥的一种特殊情况----抽屉原理。

    所以这里用二进制枚举所有情况(对应1到F),二进制对应的1,表示选择、0表示不选择;然后把1对应的位(if(i&(1<<j)))求LCM;算贡献的时候取决于1的个数。

    #include <bits/stdc++.h>
    using namespace std;
    #define LL long long
    LL a[40];
    int main(){
        LL N,M,ans=0,gd;
        scanf("%d%d",&N,&M);
        for(int i=1;i<=M;i++) {
            scanf("%d",&a[i-1]);
        }
        LL F=(1<<M)-1;
        for(int i=1;i<=F;i++){
            LL cnt=0;
            for(int j=0;j<M;j++){
                if(i&(1<<j)){
                    cnt++;
                    if(cnt==1) gd=a[j];
                    else gd=gd*a[j]/(__gcd(a[j],gd));
                }
            }
            if(cnt&1){
                ans+=N/gd;
            }
            else ans-=N/gd;
        }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    Scilab 的画图函数(2)
    Webapp的display-name问题
    记录:在老XPS1330上安装CentOS7
    包含Blob字段的表无法Export/Import
    记一段脚本的诞生
    一个短小的JS函数,用来得到仅仅包含不重复元素的数组
    然并卵
    Linux下的定时任务Crontab
    两段用来启动/重启Linux下Tomcat的Perl脚本
    JavaScript中给二维数组动态添加元素的质朴方法
  • 原文地址:https://www.cnblogs.com/hua-dong/p/13599577.html
Copyright © 2020-2023  润新知