2019CCPC哈尔滨站部分题解

Fixing Banners(DFS)

比赛链接:https://codeforces.com/gym/102394/problem/F

题目大意

现在有 6 6 6个字符串,你需要从每一个字符串中各取出一个字符,最后拼成"harbin"(顺序无所谓,只要最后得到的 6 6 6个字符是这 6 6 6个即可)。
问是否可以完成任务,可以输出" Y e s Yes Yes",否则输出" N o No No"。

思路

第一想法就是DFS硬模拟,从第一个字符串开始搜,T掉了。

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

string flag;
map<char,int> mp;
bool vis[10];
string s[10];
void init()
{
    mp['h']=1;
    mp['a']=2;
    mp['r']=3;
    mp['b']=4;
    mp['i']=5;
    mp['n']=6;
}

void dfs(int deep)
{
    if(deep==6){
        flag="Yes";
        return;
    }
    for(int i=0;i<s[deep].size();i++)
    {
        if(mp[s[deep][i]]>=1&&mp[s[deep][i]]<=6&&!vis[mp[s[deep][i]]])
        {
            vis[mp[s[deep][i]]]=true;
            dfs(deep+1);
            vis[mp[s[deep][i]]]=false;
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    init();
    while(t--)
    {
        for(int i=0;i<6;i++)
            cin>>s[i];
        flag="No";
        memset(vis,false,sizeof(vis));
        dfs(0);
        cout<<flag<<endl;
    }
}

效率低的原因是在dfs()函数中,我每次都是遍历整个字符串。假设每个字符串中的字母都有效(即每个字符串都是完全由’h'、'a'、'r'、'b'、'i'、'n'组成),那我这时间复杂度直接起飞。
所以需要优化。

我们看字符串hhhhhhhh,假设我们选中了第一个h之后进行搜索,结果搜索失败。那么在它之后的h一定也都是失败的。
这里我们就可以用空间换时间,定义二维数组m[10][10]

m[i][j]=1:第i个字符串含有'harbin'中的第j个字符
m[i][j]=0:第i个字符串不含有'harbin'中的第j个字符

这样我们在dfs()函数中就无需遍历字符串的全部,只需要遍历一个长度为 6 6 6的一维数组,时间复杂度大大减小。

AC代码

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

string flag;
string s[10];
map<char,int> mp;
bool vis[10];
int m[10][10];

void init()
{
    mp['h']=1;
    mp['a']=2;
    mp['r']=3;
    mp['b']=4;
    mp['i']=5;
    mp['n']=6;
}

void dfs(int deep)
{
    if(deep==6)
    {
        flag="Yes";
        return;
    }
    for(int i=1; i<=6; i++)
    {
        if(m[deep][i]&&!vis[i])
        {
            vis[i]=true;
            dfs(deep+1);
            vis[i]=false;
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    init();
    while(t--)
    {
        for(int i=0;i<6;i++)
            for(int j=1;j<=6;j++)
            m[i][j]=0;
        for(int i=0; i<6; i++)
        {
            cin>>s[i];
            for(int j=0; j<s[i].size(); j++)
                if(mp[s[i][j]])
                    m[i][mp[s[i][j]]]=1;
        }
        flag="No";
        for(int i=1; i<=6; i++)
            vis[i]=false;
        dfs(0);
        cout<<flag<<endl;
    }
}

Interesting Permutation (构造序列+思维)

比赛链接:https://codeforces.com/gym/102394/problem/I

题目大意

数组 a a a 1 1 1 ~ n n n的一种排列组合,它与数组 h h h的关系如下:

  1. l e n g t h ( a ) = l e n g t h ( h ) = n length(a)=length(h)=n length(a)=length(h)=n
  2. h i = m a x ( a 1 , a 2 , . . . , a i ) − m i n ( a 1 , a 2 , . . . , a i ) ; h_i=max(a_1,a_2,...,a_i)-min(a_1,a_2,...,a_i); hi=max(a1,a2,...,ai)min(a1,a2,...,ai)

现给出数组 h h h,请求出有多少个不同的数组 a a a符合条件。
答案对1e9+7取模。

思路

构造题讲究的就是一个思路清晰,阿巴博主在看完第一遍题目之后就已经大脑空白了。
不过这点难度博主还是可以接受的,构造题再怎么说也比什么"后缀数组+RMQ+主席树"让人看着舒服一些。
在这里插入图片描述
我们先根据a数组的特点,推断出正常的 h h h数组应该满足什么条件:

  1. h [ 1 ] = 0 h[1]=0 h[1]=0
  2. 数组 h h h单调不递减;
  3. m a x ( h 1 , h 2 , . . . , h n ) = n − 1 max(h_1,h_2,...,h_n)=n-1 max(h1,h2,...,hn)=n1

排除以上三点之后,就需要想办法求出数组 a a a的方案数了。

  • 定义ans当前的方案数,初始值为 1 1 1

  • 定义cnt当前可用的中间数的数量中间数的意思就是当前的最大数与最小数之间数字的个数。当n=1时, h 1 = 0 h_1=0 h1=0,此时最大值与最小值相同,所以初始值为 0 0 0

  • 如果h[i]==h[i-1],说明a[i]是处于a[1]~a[i-1]中最大数与最小数之间的一个数字,那么方案数就变成ans*cnt。又由于我们需要借用cnt个中间数中的一个,所以cnt--

  • 如果h[i]>h[i-1],则说明a[i]a[1]~a[i]之间的最大数/最小数,此时a[i]为最大值是ans个方案,a[i]为最小值也是ans个方案,所以方案数变为2*ans

    • 如果a[i]为最大值,设a[1]~a[i-1]的最大数为mx,则中间数会多出a[i]-mx-1个,而这个a[i]-mx-1其实就是h[i]-h[i-1]-1
      在这里插入图片描述
    • 同理,当a[i]为最小值时,中间数仍然会多出h[i]-h[i-1]-1个,所以cnt+=h[i]-h[i-1]-1

所以说博主真的很不擅长构造题,真滴烦。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e5+100;

ll h[maxn];

int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        int n,flag=1;
        cin>>n;
        h[0]=-1;
        for(int i=1; i<=n; i++)
        {
            cin>>h[i];
            if(h[i]<h[i-1]||h[i]>=n||h[1]!=0)
                flag=0;
        }
        if(flag&&h[n]==n-1)
        {
            ll ans=1;
            ll cnt=0;
            for(int i=2; i<=n; i++)
            {
                if(h[i]>h[i-1])
                {
                    ans=(ans*2)%mod;
                    cnt+=h[i]-h[i-1]-1;
                }
                else
                {
                    ans=(ans*cnt)%mod;
                    cnt--;
                }
            }
            cout<<ans<<endl;
        }
        else
            cout<<"0"<<endl;
    }
}

Justifying the Conjecture (思维)

比赛链接:https://codeforces.com/gym/102394/problem/J

题目大意

现给出一个正整数 n n n,请问 n n n是否可以被分成一个素数 x x x与一个合数 y y y,使得 x + y = n x+y=n x+y=n
如果可以,请输入任意一组符合条件的 x 、 y x、y xy;否则输出 − 1 -1 1

思路

众所周知(并不是):

  1. 素数之中只有 2 2 2是偶数,其他的素数都是奇数;
  2. 1 1 1既不是素数也不是合数;

这是一道特判题,而做特判题的技巧就是找最简单的方案,所以我们在这里就要从这个特殊的素数 2 2 2 下手。
先把一些特殊情况踢出掉:1,2,3,4,5这五个数是无法拆分的,原因自己想。
那么除去这五个数之后,我们把剩下的数拆分为奇数偶数

  • 如果n为奇数,即 x + y x+y x+y为奇数,此时只有一种可能:奇数+偶数=奇数
    我们取 x = 3 x=3 x=3,则 n − x n-x nx就一定是一个不等于 2 2 2的偶数,那么 n − x n-x nx就一定是一个合数;
  • 如果n为偶数,即 x + y x+y x+y为偶数,此时有两种可能:奇数+奇数=偶数 / 偶数+偶数=偶数
    如果取 y y y为奇数,我们还需要额外判断 y y y是否为合数;
    如果取 y y y为偶数,只要 y ! = 2 y!=2 y!=2 y y y就一定是合数;

AC代码

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

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        if(n<=5) cout<<"-1"<<endl;
        else if(n&1)
            cout<<"3"<<" "<<n-3<<endl;
        else
            cout<<"2"<<" "<<n-2<<endl;
    }
}

Keeping Rabbits(水题)

比赛链接:https://codeforces.com/gym/102394/problem/K

题目大意

现在DreamGrid养了n只兔子,第 i i i只兔子的初始体重为 w i w_i wi。每只兔子每天会增重 w i ∑ j = 1 n w j \frac{w_i}{\sum_{j=1}^{n}w_j} j=1nwjwi
请求出k天后每只兔子的重量。

思路

这都能卡住的吗少年?
在这里插入图片描述

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;

double a[maxn];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        double sum=0;
        for(int i=1;i<=n;i++){
            scanf("%lf",&a[i]);
            sum+=a[i];
        }
        for(int i=1;i<=n;i++){
            a[i]+=k*1.0*a[i]/sum;
        }
        for(int i=1;i<=n;i++)
        {
            if(i!=1) printf(" ");
            printf("%.8f",a[i]);
        }
        printf("\n");
    }
}

后话

感谢阅读,希望能对你产生一点用处。

以下台词取自《银魂》第104集——真选组动乱篇:
(每个人的心里都藏着一个死小孩。)

在这里插入图片描述

"无法接受孤独,而将孤独的责任归咎他人"
"害怕被人拒绝,于是就先自己拒绝自己"
"害怕受到伤害,于是演着喜欢孤独的戏"
"心里逐渐形成了堡垒"
"唯独想将才华昭示天下的表现欲日益膨胀"
"希望得到他人的认可"
"即使得到认可也总是得不到满足"
"『我能更有所成就,我和你们不一样』"
"『我要让你们彻底体会到,我的存在』"
"不知不觉中我已然忘记,我真的想得到的是什么"
"不是地位、名誉、武功才能这些"
"甚至不是什么能理解自己的人"
"我只不过是,希望有人能在我身边"
"只是,希望能有人关注我"
"只是讨厌独自一人"
"只是想要能从正面真正接受我的同伴"
"只是想要能从正面和我交手的人"
"只是想要得到同伴而已"

吾日三省吾身:日更否?刷题否?快乐否?
更新了,但不是日更;已刷;平静
路漫漫其修远兮,吾将上下而求索

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值