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\n",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\n",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\n",ans);
    return 0;
}

 

posted @ 2020-09-02 00:08  nimphy  阅读(1975)  评论(5编辑  收藏