2019牛客暑期多校训练营(第六场)菜鸡的题解

2019牛客暑期多校训练营(第六场)


# B
按题意模拟即可,我们先把二进制转成10进制,最后用自带的16进制输出就好了,不用我们自己去把10进制转16进制。

#include
using namespace std;
char s[1000];
int a[100];
int main()
{
    int t;
    scanf("%d",&t);
    for(int tt=1;tt<=t;tt++)
    {
        scanf("%s",s);
        printf("Case #%d: ",tt);
        for(int i=0;i<8;i++)
        {
            a[i]=0;
            for(int j=i*16;j<(i+1)*16;j++)
            {
                a[i]=a[i]*2+(s[j]-'0');//先转成10进制
            }
        }
        int st=0,maxlen=0;//st是起点位置
        for(int i=0;i<8;i++)
        {
            if(a[i]==0)
            {
                for(int j=i;j<8;j++)
                {
                    if(a[j]==0)
                    {
                        if(j-i+1>maxlen||(j-i+1==maxlen&&(j!=7||st==0)))//相同长度时后面缩减对答案的贡献更高
                        {//因为 ::比:0小
                            maxlen=j-i+1;//更新起点和长度
                            st=i;
                        }
                    }
                    else
                        break;
                }
            }
        }
        if(maxlen<2)
        {
            for(int i=0;i<8;i++)
                printf("%x%c",a[i],i==7?'\n':':');//%x是16进制输出
        }
        else
        {
            for(int i=0;i<st;i++)
                printf("%x:",a[i]);//先输出起点前的
            printf(":");//缩减
            if(st==0)
                printf(":");//特判
            if(st+maxlen==8)//结尾输出换行
                printf("\n");
            for(int i=st+maxlen;i<8;i++)
                printf("%x%c",a[i],i==7?'\n':':');
        }
    }

# D
开始看像二分,越看越像二分,结果数据太水,暴力可过

#include
using namespace std;
const int maxn=1e3+5;
int a[maxn],k,n,sum;
bool vis[maxn];//表示是否访问过
bool check(int num)
{
    for(int i=0;i<n;i++)vis[i]=0;//初始化全部没访问过
    int kk=k,left=n;//left标记还有多少没被放进去
    while(kk--)
    {
        int now=num;
        for(int i=n-1;i>=0;i--)
        {
            if(now<a[0])//不能再放入了,退出
                break;
            if(now-a[i]>=0&&!vis[i])//没被放入且可放入
            {
                vis[i]=1;
                now-=a[i];
                left--;
            }
        }
    }
    if(!left)//left==0,全都被放入
        return true;
    else
        return false;
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int q=1;q<=t;q++)
    {
        sum=0;
        scanf("%d%d",&n,&k);
        for(int i=0;i<n;i++)
        {
            scanf("%d",a+i);
            sum+=a[i];//计算总和
        }
        sort(a,a+n);//从小到大排序,check中从大到小遍历,模拟题意中每次都选最大的放进去
        for(int i=max((int)ceil(sum*1.0/k),a[n-1]);;i++)//确认盒子体积的下界,从下界往上遍历即可
        {
            if(check(i))//传体积
            {
                printf("Case #%d: %d\n",q,i);
                break;
            }
        }
    }
}

# G
题意就是给你个加密的集合,恢复成原来的样子,如果不能恢复,就输出Impossible,否则输出相应加密的值。可以建立一个数组,然后不断进行全排列,而且要和加密集合匹配看是不是满足星期五的条件,这里要注意特判闰年的情况还有年份月份不合法。

#include
#define ll long long 
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ios(x) ios::sync_with_stdio(x)
const int maxn=1e5+5;
using namespace std;
int t,n;
char A,B,C,D,E,F,G,H,I,J;
string str[maxn];
int leap[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};//leap year,闰年
int nonleap[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};//nonleao year,平年
int whatDay(int day,int month,int year)//蔡勒公式计算日期
{
    int ans;
    if(month==1||month==2)//蔡勒公式month范围是[3,14]
    {//1是上一年的13月,2是上一年的14月
        month+=12;
        year--;
    }
    /*if((year<1752)||(year==1752&&month<9)||(year==1752&&month==9&&day<3))
        ans=(day+2*month+3*(month+1)/5+year+year/4+5)%7;
    else
        ans=(day+2*month+3*(month+1)/5+year+year/4-year/100+year/400)%7;*/
    ans=(day+2*month+3*(month+1)/5+year+year/4-year/100+year/400)%7;
    //试了很多次,这题好像没有区分1752前后蔡勒公式的版本
    return ans;//星期(ans+1)
}
int main()
{
    sc(t);
    for(int tt=1;tt<=t;tt++)
    {
        sc(n);
        for(int i=1;i<=n;i++)
        {
            getchar();//换行
            str[i].resize(8);//分配内存
            scanf("%c%c%c%c%c%c%c%c%c%c",&A,&B,&C,&D,&E,&F,&G,&H,&I,&J);
            str[i][0]=A,str[i][1]=B,str[i][2]=C,str[i][3]=D,str[i][4]=F,str[i][5]=G,str[i][6]=I,str[i][7]=J;
        }
        printf("Case #%d: ",tt);
        sort(str+1,str+1+n);
        int maxx=unique(str+1,str+1+n)-(str+1);//去重
        int a[10]={0,1,2,3,4,5,6,7,8,9};//用a中值表示char对应值
        bool judge1=false;//记录是否能找到
        do
        {
            bool judge2=true;
            for(int i=1;i<=maxx;i++)
            {
                if(judge2==false)
                    break;
                int year,month,day;
                year=a[str[i][0]-'A']*1000+a[str[i][1]-'A']*100+a[str[i][2]-'A']*10+a[str[i][3]-'A'];
                if(year<1600||year>9999)//超范围
                {
                    judge2=false;
                    break;
                }
                month=a[str[i][4]-'A']*10+a[str[i][5]-'A'];
                if(month<1||month>12)
                {
                    judge2=false;
                    break;
                }
                day=a[str[i][6]-'A']*10+a[str[i][7]-'A'];
                if(year%400==0||(year%4==0&&year%100!=0))//如果是闰年
                {
                    if(day<1||day>leap[month])
                    {
                        judge2=false;
                        break;
                    }
                    /*if(month==1||month==2)
                    {
                        month+=12;year--;
                    }
                    int w=(day+2*month+3*(month+1)/5+year+year/4-year/100+year/400)%7;
                    if(w!=4)
                    {
                        judge2=false;
                        break;
                    }*/
                    if(whatDay(day,month,year)!=4)//蔡勒
                    {
                        judge2=false;
                        break;
                    }
                }
                else//不是闰年
                {
                    if(day<1||day>nonleap[month])
                    {
                        judge2=false;
                        break;
                    }
                    if(month==1||month==2)
                    {
                        month+=12;year--;
                    }
                    /*if(month==1||month==2)
                    {
                        month+=12;year--;
                    }
                    int w=(day+2*month+3*(month+1)/5+year+year/4-year/100+year/400)%7;
                    if(w!=4)
                    {
                        judge2=false;
                        break;
                    }*/
                    if(whatDay(day,month,year)!=4)//蔡勒
                    {
                        judge2=false;
                        break;
                    }
                }
            }
            if(judge2==true)//找到符合的排列
            {
                judge1=true;
                break;//退出while
            }
        }while(next_permutation(a,a+10));
        //生成下一个排列,返回bool,如果不存在则返回false
        if(judge1==true)//有符合的排列
        {
            for(int i=0;i<10;i++)
                printf("%d",a[i]);
        }
        else
        {
            printf("Impossible");
        }
        printf("\n");
    }
}

# H
vector存图,在两次bfs中确认集合A还有集合B到图上每一个点的距离,d[][][]中第一个是表示集合AB,第二个表示集合中的点,第三个表示图上的点,两次bfs确定了单点的最小距离,然后确认双点的最小距离,用dis[i]表示到i的总距离,然后松弛一遍让距离变成最小,每种情况的概率都是一样的,所以我们的期望可以这样计算:
E(x)=sigma(i=1到i=sa×sb×n) Xi/pi
=[sigma(i=1到i=sa×sb×n) Xi]/p
=X/p
pi是每种情况的概率(相同的),i的上界是情况的总数,sa是A集合元素个数,sb是B集合元素个数,sa×sb×n就是总情况数
然后我们就可以计算X和p,最后约分一下就好了.

#include
using namespace std;
#define ll long long
#define sc(x) scanf("%d",&x)
int t,n,m;
ll fz=0,fm=0;
const int maxn=1e5+5;
vector<int> vec[maxn];//存边
int d[2][21][maxn],dis[maxn];
bool vis[maxn];
vector<int> vt[maxn];//存点
void bfs(int o,int d[])
{
    vector<int> q;
    q.push_back(o);
    int now=0;
    for(int i=0;i<=n+1;i++)
        d[i]=-1;
    d[o]=0;
    while(now<q.size())//没遍历完
    {
        int oo=q[now++];
        for(int i : vec[oo])//范围for循环遍历oo能到的所有点
        {
            if(d[i]==-1)
            {
                d[i]=d[oo]+1;
                q.push_back(i);//bfs搜索
            }
        }
    }
}
ll cal_dis(int u,int v)
{
    for(int i=1;i<=n;i++)
        dis[i]=d[0][u][i]+d[1][v][i];//总距离
    memset(vis,0,sizeof(vis));
    for(int i=0;i<=m;i++)
        vt[i].clear();
    for(int i=1;i<=n;i++)
        vt[dis[i]].push_back(i);//存距离为dis[i]的所有点i
    for(int i=0;i<=m;i++)
    {
        while(vt[i].size())
        {
            int from=vt[i].back();vt[i].pop_back();//每次取出一个点
            if(vis[from])
                continue;
            for(int to :vec[from])//范围for遍历当前点的下一个点
            {
                if(dis[to]>dis[from]+1)//图论的松弛操作,找到最小距离
                {
                    dis[to]=dis[from]+1;
                    vt[dis[to]].push_back(to);
                }
            }
            vis[from]=true;
        }
    }
    long long return_ans=0;//也可以用accumulate函数(dalao做法)
    for(int i=1;i<=n;i++)
    {
        return_ans+=dis[i];
    }
    return return_ans;
}
int main()
{
    sc(t);
    int cnt=t;
    while(t--)
    {
        sc(n);sc(m);
        for(int i=1;i<=n;i++)
            vec[i].clear();//初始化
        for(int i=1;i<=m;i++)
        {
            int u,v;
            cin>>u>>v;
            vec[u].push_back(v);//存边
            vec[v].push_back(u);//无向图存双向边
        }
        int s1;sc(s1);
        for(int i=0;i<s1;i++)//集合A
        {
            int sa;
            sc(sa);
            bfs(sa,d[0][i]);
        }
        int s2;sc(s2);
        for(int i=0;i<s2;i++)//集合B
        {
            int sb;
            sc(sb);
            bfs(sb,d[1][i]);
        }
        fz=0;//答案的分子
        for(int i=0;i<s1;i++)
        {
            for(int j=0;j<s2;j++)
            {
                fz+=cal_dis(i,j);
            }
        }
        fm=1LL*s1*s2*n;//分母,也就是情况总数,作为期望计算的概率
        ll gcdd=__gcd(fz,fm);//__gcd是求公约数的库函数
        fz/=gcdd;fm/=gcdd;//化简
        printf("Case #%d: ",cnt-t);
        if(fm==1)
            printf("%lld\n",fz);
        else
            printf("%lld/%lld\n",fz,fm);
    }
}

# L
先预处理好每个科技升到j级时的最优选择,然后遍历最低等级,全部在j级以上的和加起来减去其中最小的那个(不升级,保证最小的是j级)就是答案,所有答案取最优即可
dalao代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define pf printf
#define INF 0x3f3f3f3f
#define sc(x) scanf("%d", &x)
#define scs(x) scanf("%s", x)
#define scd(x) scanf("%lf", &x)
#define scl(x) scanf("%lld", &x)
#define mst(a,x) memset(a, x, sizeof(a))
#define rep(i,s,e) for(int i = s; i < e; i++)
#define dep(i,e,s) for(int i = e; i >= s; i--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> VI;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int seed = 131;
const int maxn = 1e3 + 5;
const int mod = 1e9 + 7;
const double eps = 1e-10;
const double PI = acos(-1.0);
int m,n,k,t;
ll a[maxn][maxn],b[maxn];
ll ans[maxn][maxn];//第i个科技升到j级以上的最优选择
int main() {
    sc(t);
    int cas=0;
    while(t--){
        sc(n); sc(m);
        rep(i,1,n+1) rep(j,1,m+1) scl(a[i][j]),a[i][j]=-a[i][j];
        rep(i,1,m+1) scl(b[i]);
        rep(i,1,n+1){
            ans[i][m]=0;
            dep(j,m-1,0) ans[i][j]=max(0ll,a[i][j+1]+ans[i][j+1]);
        }
        ll tmp=0,aa=0;
        rep(j,0,m+1){//最低等级为j,遍历j
            rep(i,1,n+1) tmp+=a[i][j];
            tmp+=b[j];
            ll mn=1e18,tt=0;//tt是全都在j级以上
            rep(i,1,n+1) tt+=ans[i][j],mn=min(mn,ans[i][j]);//有一个不升级,减去mn,赚的最少的那个
            aa=max(aa,tmp+tt-mn);
        }
        pf("Case #%d: ",++cas);
        pf("%lld\n",aa);
    }
}

你可能感兴趣的:(dp,模拟,图论)