牛客小白赛23

A.不会状压,用dfs,n比较小,枚举是否消灭某一行,消灭行数上限判断 还需要消灭多少列,多处用剪枝。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define inf 0x3f3f3f3f
const double pi=3.1415926;
const double eps=1e-9;
using namespace std;
 
 
int n,m,a,b;
char s[25][100005];
int l[100005];///列上敌人的总数
int vis[25];///0表示没有该行没有消灭 1表示该行消灭了
bool flag;
 
bool check()
{
    memset(l,0,sizeof(l));
    int res=0;///还需要消灭的列数
    for(int i=0;i<n;i++)
    {
        if(vis[i]==0)///只需要计算没有被消灭的行
        {
            for(int j=0;j<m;j++)
                if(s[i][j]=='*' && l[j]==0 )
                {
                    res++;
                    l[j]=1;
                }
        }
    }
    if(res<=b)
        return true;
    else
        return false;
}
 
 
void dfs(int x,int num)///当前行  已经消灭的的行的数量
{
    if(x==n)
    {
        if(a==num && check())///尽量消灭多的行,技能不用白不用,免得check太多超时
            flag=true;
        return;
    }
    if(flag)///一有机会就剪枝
        return;
    vis[x]=0;
    dfs(x+1,num);///此行不消灭
    if(flag)///一有机会就剪枝
        return;
    if(num<a)///还有消灭行的次数,此行消灭
    {
        vis[x]=1;
        dfs(x+1,num+1);
    }
}
 
 
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(vis,0,sizeof(vis));
        flag=false;
        scanf("%d%d%d%d", &n, &m, &a, &b);
        getchar();
        for(int i=0;i<n;i++)
            scanf("%s",s[i]);
        getchar();
        dfs(0,0);
        if(flag)
            printf("yes\n");
        else
            printf("no\n");
 
    }
 
    return 0;
}
A

B:先用欧拉筛打出素数表,唯一分解定理对p分解质因数,对每一个质因数x,阶乘至少要多少能解决掉这些质因数。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define inf 0x3f3f3f3f
const double pi=3.1415926;
const double eps=1e-9;
using namespace std;
 
const int maxx=1000005;
int prime[maxx];
int vis[maxx];
int num[32005];
int cnt;
int p;
 
void init()
{
    memset(vis,true,sizeof(vis));
    vis[0]=vis[1]=false;
    cnt=0;
    for(int i=2;i<=maxx;i++)
    {
        if(vis[i])
            prime[cnt++]=i;
        for(int j=0;j<cnt && prime[j]*i<=maxx;j++)
        {
            vis[ i*prime[j] ]=false;
            if(i%prime[j]==0) break;
        }
    }
}
 
int find(int k,int x)///找k个x,阶乘至少要多少,x是素数
{
    int i=0;
    int now=0;//目前提供了多少k
    for(i=x;k>0;i=i+x){
        //printf("i=%d k=%d x=%d\n",i,k,x);
        int temp=i;
        while(temp%x==0){
            k--;
            temp=temp/x;
 
        }
 
    }
    return i-x;
}
 
int main()///对p分解质因子
{
 
    init();/*
    int ans=find(7,2);
    printf("ans=%d\n",ans);*/
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&p);///p<1e9
        memset(num,0,sizeof(num));///质因子的数量
        int maxx=0;
        for(int i=0;prime[i]<32000 && prime[i]<=p;i++){
            int temp=0;
            while(p%prime[i]==0){
                p=p/prime[i];
                temp++;
 
            }
            maxx=max(maxx,find(temp,prime[i]));
            //printf("sushu=%d temp=%d maxx=%d\n",prime[i],temp,maxx);
        }
        if(p>0)///大素数
            maxx=max(maxx,p);
        printf("%d\n",maxx);
    }
    return 0;
}
B

C:完全图是指每两个点都有边直接相连的图,分离出第一个联通分量需要删除n-1条边,第二个需要删除n-2,以此类推直到最后两个点删除1条边。等差求和二分查找项数,变量太大开启Java大数杀招。

import java.math.BigInteger;
import java.util.*;
 
public class Main {
    public static void main(String[] args) {
        Scanner scan=new Scanner(System.in);
        BigInteger two=BigInteger.valueOf(2);
        int t=scan.nextInt();
        while(t>0) {
            t--;
            long n=scan.nextLong();
            long m=scan.nextLong();
            long l=1,r=n-1,mid=0,ans=0;;
            //完全图,任意两点都有边,分离出一个第一个连通分量要删n-1,第二个n-2
            //n-1  n-2 n-3 ... 1 等差求和  有x项
            //(n-1 + n-x) * x / 2
            while(l<=r) {
                mid=(l+r)/2;//项数
                BigInteger sum=BigInteger.valueOf(2*n-1-mid).multiply(BigInteger.valueOf(mid)).divide(two);
                //sum=(2*n-1-mid)*mid/2
                //System.out.println("l="+l+" r="+r+" mid="+mid+" sum="+sum);
                if(sum.compareTo(BigInteger.valueOf(m))<=0) {
                    ans=mid;
                    l=mid+1;
                }else
                    r=mid-1;
            } 
            System.out.println(ans+1);
        }
    }
     
}
C

E:类似这种题,把代码放到编译器运行一下会发现答案都一样。

G:记录每条边需要用多少次,用得多的赋值小一点的权值。dfs中,记录以x为根的子树的规模大小,一条边用的次数=两点的子树规模相乘。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define inf 0x3f3f3f3f
const double pi=3.1415926;
const double eps=1e-9;
using namespace std;
 
vector<int>a[100005];
int vis[100005];
int num[100005];
ll edge[100005];
int t,n,k,idx;
 
struct node{
    int x;
    int val;
};
 
void dfs(int x,int last)///当前点,父节点
{
    num[x]=1;
    int len=a[x].size();
    for(int i=0;i<len;i++){
        int v=a[x][i];
        if(v!=last){
            dfs(v,x);
            num[x]+=num[v];///以u为根节点的子树的规模
            ///edge[idx++]=1ll*num[x]*num[v];//不可以这样写,因为num[x]还在累加之中
            edge[idx++]=1ll*num[v]*(n-num[v]);///x和v这条边用过的次数=两边子树的大小相乘
 
        }
 
    }
}
 
int main()
{
    scanf("%d",&n);
    idx=0;
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        a[x].push_back(y);
        a[y].push_back(x);
    }
    dfs(1,-1);
    sort(edge,edge+idx);
    ll ans=0;
    for(int i=0;i<idx;i++)
        ans=ans+(--n)*edge[i];
    printf("%lld\n",ans);
 
    return 0;
}
G

H:全部看出二进制,手动模拟一下进位,从大到小填背包,能填满的话第一次肯定会增加到230

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define inf 0x3f3f3f3f
const double pi=3.1415926;
const double eps=1e-9;
using namespace std;
 
int two[31];
struct node{
    int x;
    int id;
    int vis;
};
 
node k[100005];
int m;
void init(){
    two[0]=1;
    for(int i=1;i<31;i++)
        two[i]=2*two[i-1];
}
 
 
 
bool cmp(node p1,node p2){
    return p1.id<p2.id;
}
 
bool cmp2(node p1,node p2){
    return p1.x<p2.x;
}
 
int main()
{
    init();
    int t;
    scanf("%d",&t);
    bool flag;
    while(t--){
        flag=false;
        scanf("%d",&m);
        for(int i=0;i<m;i++)
            scanf("%d",&k[i].x),k[i].id=i,k[i].vis=0;
        sort(k,k+m,cmp2);
        ll sum=two[30];
        ll now=0;
 
        for(int i=m-1;i>=0 && now<sum;i--){
            now+=two[ k[i].x ];
            k[i].vis=1;
            if(now==sum)
                flag=true;
        }
        if(flag){
            sort(k,k+m,cmp);
            for(int i=0;i<m;i++)
                printf("%d",k[i].vis);
            printf("\n");
        }else
            printf("impossible\n");
    }
    return 0;
}
H

I:优先比较前缀字母,相同前缀的话,后缀越长,字典序越大,暴力找片段,ans数组存储答案,每次加进一个能使字典序最大的字母。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define inf 0x3f3f3f3f
const double pi=3.1415926;
const double eps=1e-9;
using namespace std;
 
 
char s[1005];
char ans[1005];
int cnt;
int n;
 
 
bool check(int a){//起始位置
    bool flag=true;
    for(int i=1;i<=cnt;i++){
        if( a+i-1<n && ans[i]==s[a+i-1] ){
           // printf("check if  i=%d a+i-1=%d n=%d\n",i,a+i-1,n);
        }else{
            flag=false;
        }
    }
    return flag;
}
 
int main()
{
    scanf("%s",s);
    n=strlen(s);
    char maxx=s[0];
    for(int i=0;i<n;i++){
        if(s[i]>maxx)
            maxx=s[i];
    }
    ans[1]=maxx;//先放一个最大的,后续检查前缀相同,每次添加一个最大的字母
    cnt=1;
    bool flag=true;
    while(flag){
        flag=false;
        for(int i=0;i<n;i++)
        {
            if(s[i]==ans[1] && check(i))
            {//第一次只有1
                if(i+cnt<n && ans[cnt+1]<s[i+cnt])
                    ans[cnt+1]=s[i+cnt],flag=true;
            }
        }
        if(flag)
            cnt++;
    }
    for(int i=1;i<=cnt;i++)
        printf("%c",ans[i]);
    printf("\n");
    return 0;
}
I

J:最大-最小

 

posted @ 2020-03-23 15:47  守林鸟  阅读(139)  评论(0编辑  收藏  举报