Codeforces Round #390 (Div. 2) A B C D

Posted 天翎月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #390 (Div. 2) A B C D相关的知识,希望对你有一定的参考价值。

这是一场比较难的div2 ... 比赛的时候只出了AB

A很有意思 给出n个数 要求随意的把相邻的数合并成任意多数 最后没有为0的数 输出合并区间个数与区间 

可以想到0可以合到任何数上并不改变该数的性质 所以如果不全是0 最后一定是有答案的 把所有的0都合并到最近的非0数上去 非0数不变

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
int n ;
int a[105];
int l[105];
int r[105];
int main(){
    scanf("%d",&n);
    for(int i = 1 ;i<=n;i++)scanf("%d",&a[i]);
    bool ok = true;
    int res = 0;
    int cnt = 0;
    for(int i = 1; i<=n;i++){
        if(a[i]!= 0){
            cnt ++ ;
            l[cnt] = i;
            r[cnt] = i;
        }
    }
    if(cnt == 0){
        printf("NO\n");
        return 0 ;
    }
    r[0] = 0 ;
    l[cnt+1] = n + 1;
    for(int i = 1; i<= cnt ;i ++){
        l[i] = r[i-1] + 1;
        r[i] = l[i+1] - 1;
    }
    printf("YES\n");
    printf("%d\n",cnt);
    for(int i = 1 ; i<=cnt ; i ++){
        printf("%d %d\n",l[i],r[i]);
    }
}

B很无聊..下三子棋 现在该我们下x了 能否一步胜利或者不走就胜利了呢?

写一个很手动的check函数 先判断能否直接胜利 再枚举每一个可以放的位置 放下再check

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
char s[50][50];
bool check(char a){
    for(int i = 1 ; i<=4 ; i ++){
        for(int j = 1 ; j<= 2 ; j ++ ){
            if(s[i][j] == s[i][j+1] &&s[i][j+1] == s[i][j+2] && s[i][j+1] == a){
                return true;
            }
        }
    }
    for(int j = 1 ; j<=4 ; j ++){
        for(int i = 1 ; i<= 2 ; i ++ ){
            if(s[i][j] == s[i+1][j] &&s[i+1][j] == s[i+2][j] && s[i][j] == a){
                return true;
            }
        }
    }
    for(int i = 1; i<=2 ;i ++){
        if(s[i][i] == s[i+1][i+1] &&s[i+1][i+1] == s[i+2][i+2] && s[i][i] == a)return true ;
    }
    if(s[2][1] == s[3][2] && s[3][2] == s[4][3] &&s[4][3] == a)return true;
    if(s[1][2] == s[2][3] && s[2][3] == s[3][4] &&s[3][4] == a)return true;

    if(s[2][4] == s[3][3] && s[3][3] == s[4][2] &&s[4][2] == a)return true;
    if(s[1][3] == s[2][2] && s[2][2] == s[3][1] &&s[3][1] == a)return true;

    if(s[1][4] == s[2][3] && s[2][3] == s[3][2] && s[3][2] == a)return true;
    if(s[2][3] == s[3][2] && s[3][2] == s[4][1] && s[4][1] == a)return true;
     return false ;
}
int main(){
    for(int i = 1;i<=4 ; i++)scanf("%s",s[i] + 1);
    if(check(‘x‘)){
        printf("YES\n");
        return 0;
    }
    if(check(‘o‘)){
        printf("NO\n");
        return 0 ;
    }
    for(int i = 1 ; i <= 4;i++){
        for(int j = 1 ; j<=4 ;j ++){
            if(s[i][j] == ‘.‘){
                s[i][j] = ‘x‘;
                if(check(‘x‘)){
                    printf("YES\n");
                    return 0 ;
                }
                s[i][j] = ‘.‘;
            }
        }
    }
    printf("NO\n");
}

C一个比较耗费时间的字符串模拟 给出一些人的名字和他们说的话 有些话的发言人用?代替 要求 相邻的两句话不会是同一个人说的 一个人说的话不会提到自己的名字

先做一个字符串模拟 用map把人名编号 再用r[]记录每句话的发言人 用cx[][]来记录每句话中出现的人名 用ans[]来处理最后的答案 

最后我们 会得到一个cx数组 记录每句没有主人的话能选择的人

会发现贪心好像是不可做的 当前这句话 如果我们做出一些选择 可能会影响到下一个人的选择

搜索的话 如果100句话都是?也会超时

可以利用cx[][]数组和r[]数组 做一个dp题

如果想不到怎么dp 可以去代码中寻找注释~

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
string name[1050];
map<string , int >p;
int n , m ;
char s[1050];
string ans[1050];
int r[1050];
bool cx[105][105];

int cnt ;
int us[105];

int main(){
    int t;
    scanf("%d",&t);
    while(t -- ){
        memset(r , -1 ,sizeof(r));
        scanf("%d",&n);
        p.clear();
        string tm = "";
        for(int i = 1; i<=n ;i ++){
            scanf("%s",s);
            int len = strlen(s);
            tm = "" ;
            for(int j = 0 ; j<len ;j ++)tm += s[j];
            p[tm] = i ;
            name[i] = tm ;
        }
        scanf("%d",&m);
        getchar();
        bool ok = true;
        memset(cx , false,sizeof(cx));
        for(int i = 1; i<=m;i ++){
            gets(s);
            int len = strlen(s);
            ans[i] = "";
            if(s[0] == ‘?‘){
                r[i] = -1;
                for(int j = 2 ; j<len ; j ++){
                    tm = "";
                    while(j < len && s[j] != ‘.‘ && s[j] != ‘,‘ && s[j] != ‘!‘ && s[j] != ‘?‘ && s[j] != ‘ ‘){
                        tm += s[j] ;
                        j ++ ;
                    }
                    if(tm != ""){
                        int z = p[tm];
                        if(z != 0){
                            cx[i][z] = true ;
                        }
                    }
                }
                for(int j = 1 ; j < len ; j ++)ans[i] += s[j] ;
            }
            else {
                tm = "" ;
                int j ;
                for(j = 0; s[j] !=‘:‘ ; j ++ ){
                    tm += s[j];
                }
                r[i] = p[tm];
                j ++ ;
                for(int k = 0 ; k < len ;k ++)ans[i] += s[k] ;
                for(; j<len ; j ++){
                    tm = "";
                    while(j < len && s[j] != ‘.‘ && s[j] != ‘,‘ && s[j] != ‘!‘ && s[j] != ‘?‘ && s[j] != ‘ ‘){
                        tm += s[j] ;
                        j ++ ;
                    }
                    if(tm != ""){
                        int z = p[tm];
                        if(z != 0){
                            cx[i][z] = true ;
                        }
                    }
                }
            }
         }
         if(ok == false) {
            printf("Impossible\n");
            continue ;
         }
         r[0] = r[m+1] = -1 ;
         /**
            r[i] 第几句话发言人的编号 -1代表?
            name[] 1-n号人的名字
            cx[u][v] 第u句话中提到了v

         */
         bool dp[105][105] ;
         int pre[105][105] ;
         /**
            dp[i-1][!j] == true  -> dp[i][j] = true
            pre[i][j] = pre[i-1][!j]
         */
         memset(dp , false , sizeof(dp)) ;
         for(int i = 1 ; i <= n ; i ++ )dp[0][i] = true ;
         for(int i = 1 ; i <= m ; i ++ ){
            if(r[i] != -1) {
                if(i == 1) {
                    dp[i][r[i]] = true ;
                    continue ;
                }
                for(int j = 1; j <= n ; j ++ ){
                    if(j != r[i] && dp[i-1][j] == true) {
                        dp[i][r[i]] = true ;
                        pre[i][r[i]] = j ;
                    }
                }
                continue ;
            }
            for(int j = 1; j <=n ; j ++ ){
                if(cx[i][j] == true) continue ;
                if(j == r[i+1]) continue ;
                if(j == r[i-1]) continue ;
                for(int k = 1; k <= n ; k ++ ){
                    if(j == k && i != 1)continue ;
                    if(dp[i-1][k] == true){
                        dp[i][j] = true ;
                        pre[i][j] = k ;
                    }
                }
            }
         }
         /// 利用pre数组反转录入答案
         cnt = m ;
         bool fin = false ;
         for(int i = 1; i <= n ;i ++ ){
            if(dp[m][i] == true){
                us[cnt] = i ;
                int w = i;
                while(cnt >= 2) {
                    int k = pre[cnt][w] ;
                    cnt -- ;
                    us[cnt] = k ;
                    w = k ;
                }
                fin = true ;
                break ;
            }
         }
         if(fin == false){
            printf("Impossible\n");
            continue ;
         }
         ///ans
         for(int i = 1; i <= m ; i ++ ){
            if(r[i] != -1){
                cout << ans[i] << endl ;
            }
            else {
                cout << name[us[i]] << ans[i] << endl ;
            }
         }
    }
}

D给出n个区间 要求必须选择k个区间 使这k个区间的交集最大

k个区间的交集形成的区间 它的l是这几个区间中的最大的l 而r是这几个区间中的最小的r

应当选择k个区间 使它们有最小的l和最大的r 所以动态的去想 我们可以将l从小往大选 并选择最大化的r 维护一个ans

先进行排序 使l尽可能的小 在其基础上r尽可能的小 这样 我们从1-n枚举区间 就可以直接得到最大的l了

因为我们选择的l 是逐渐增大的 所以为了保存足够大的r 需要一个优先队列来做 维护一个 < 的优先队列 存放每个区间的r

1-n枚举区间的过程 每次我们选择一个区间 向优先队列中压入它的r 如果当前队列的size < k 说明未达到题目要求 暂且不管

如果size > k  我们就q.pop 弹出当前最小的r 由于我们是动态维护了ans 所以 如果弹出最小的r 使我们失去了一个l 

1 如果这个l 是当前区间最大的l 那么当前结果已经保留进了ans 

2 如果这个l 不是当前区间最大的l 那么这个区间的移除会使ans增大

如果size == k 这个状态可能是size < k 或者 size > k 来的 如果是前者 我们就需要开始更新ans(事实上这一幕只会在第k个数字出现) 

如果是后者 我们就需要分析 这个新区间 会不会已经被弹出去了?

1 弹出去了 那么现在其实没变化 不需要做什么改变

2 没弹出去 那么现在就需要更新一下答案 我们可以肯定 当前的交集区间的l 一定是新区间的l(sort的结果) 而r 就是优先队列的top 所以 res = q.top - 新区间.l + 1

判断是否弹出 需要判断新区间的r与top的关系

有一个问题是 新区间的r 可能已经被弹出去了 当前区间并没有加进去 但是因为优先队列有r=新区间.r 所以被误判

其实是不会造成影响的 因为对于这种情况 一定会有过去的res >= 现在的res

所以这样模拟完最后就可以得出ans了 

题目要求输出一下区间的选择 

如果ans是0 随便输出k个数就可以了 

如果不是的话 由于我们不确定当前解是不是最优解 所以 过程中我们无法更新区间数的ans

但是我们可以在优先队列中 伴随r加上一个id 然后再跑一遍枚举 当res == ans的时候 队列里的id就是答案啦

聪明的你一定看到这里就完全理解了吧

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
int n , m ;
struct node {
    int l , r ;
    int id ;
    bool operator < (const node &a )const{
        return a.r < r ;
    }
}a[300050];
bool cmp(node a, node b ){
    if(a.l == b.l )return a.r < b.r ;
    else {
        return a.l < b.l ;
    }
}
int c[300050] ;
int main(){
    scanf("%d%d",&n, &m );
    for(int i = 1; i <= n ;i ++ ){
        scanf("%d%d",&a[i].l , & a[i].r ) ;
        a[i].id = i ;
    }
    sort(a+1,a+1+n,cmp) ;
    int ans = 0 ;
    priority_queue<node>q;
    int l = 0;
    for(int i = 1; i <= n ; i ++ ){
        node b = a[i] ;
        q.push(b) ;
        if(q.size() < m){
            l = a[i].l ;
        }
        if(q.size() > m){
            q.pop() ;
        }
        if(q.size() == m){
            if(b.r >= q.top().r){
                int res = q.top().r - b.l + 1 ;
                if(res > ans)ans = res ;
                l = b.l ;
            }
        }
    }
    printf("%d\n",ans) ;
    if(ans == 0){
        for(int i = 1; i <= m ; i ++ ){
            printf("%d",i);
            if(i == m)printf("\n");
            else printf(" ");
        }
    }
    else {
        while(!q.empty())q.pop();

        for(int i = 1; i <= n ; i ++ ){
            node b = a[i] ;
            q.push(b) ;
            if(q.size() < m){
                l = a[i].l ;
            }
            if(q.size() > m){
                q.pop() ;
            }
            if(q.size() == m){
                if(b.r >= q.top().r){
                    int res = q.top().r - b.l + 1 ;
                    if(res == ans){
                        int z = q.size();
                        while(!q.empty()){
                            c[z] = q.top().id ;
                            q.pop() ;
                            z-- ;
                        }
                        sort(c+1,c+1+m);
                        for(int j = 1; j <=m ;j ++ ){
                            printf("%d",c[j]);
                            if(j == m)printf("\n");
                            else printf(" ");
                        }
                        break ;
                    }
                }
            }
        }
    }
}

学车辛苦啦 笨蛋摸头~

以上是关于Codeforces Round #390 (Div. 2) A B C D的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #390 (Div. 2) B

Codeforces Round #390 (Div. 2)

Codeforces Round #390(div 2)

Codeforces Round #390 (Div. 2) 解题报告

Codeforces Round #390 (Div. 2) A B C D

Codeforces Round #436 E. Fire(背包dp+输出路径)