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 ;
                    }
                }
            }
        }
    }
}

学车辛苦啦 笨蛋摸头~

时间: 2024-12-21 21:49:30

Codeforces Round #390 (Div. 2) A B C D的相关文章

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

时隔一个月重返coding…… 期末复习了一个月也不亏 倒是都过了…… 就是计组61有点亏 复变68也太低了 其他都还好…… 假期做的第一场cf 三道题 还可以…… 最后room第三 standing383简直人生巅峰…… 看楼上楼下都是两道题的 如果A题不错那么多估计能进前300了吧…… 这场倒是把之前两场的分加回来了 开头不错 这个假期争取紫名~ A.Lesha and array splitting 把给定的数组分割成几个区间 要求各个区间和不能为0 一开始没注意到分割之后的区间重新合成之

Codeforces Round #390 (Div. 2)

22:35-0:35  1.6.2017 A.Lesha and array splitting 题意:自己看 题解: 构造什么的最弱了 想了想,貌似除了0每个数单独一组就可以,只要有一个非0数则一定可以有解 0的话不停往前找到第一个非0然后合为一组 第一个数是0怎么办?先让第一个数往后找一个非0呗 比赛的时候智商骤减,写的代码好难看还写了好长时间,并且还WA一次...应该可以很简洁的吧 #include<iostream> #include<cstdio> #include<

Codeforces Round #390 (Div. 2) B

Ilya is an experienced player in tic-tac-toe on the 4?×?4 field. He always starts and plays with Xs. He played a lot of games today with his friend Arseny. The friends became tired and didn't finish the last game. It was Ilya's turn in the game when

Codeforces Round #390 (Div. 2) D

All our characters have hobbies. The same is true for Fedor. He enjoys shopping in the neighboring supermarket. The goods in the supermarket have unique integer ids. Also, for every integer there is a product with id equal to this integer. Fedor has 

Codeforces Round #390 (Div. 2) A

One spring day on his way to university Lesha found an array A. Lesha likes to split arrays into several parts. This time Lesha decided to split the array A into several, possibly one, new arrays so that the sum of elements in each of the new arrays

Codeforces Round #279 (Div. 2) ABCD

Codeforces Round #279 (Div. 2) 做得我都变绿了! Problems # Name     A Team Olympiad standard input/output 1 s, 256 MB  x2377 B Queue standard input/output 2 s, 256 MB  x1250 C Hacking Cypher standard input/output 1 s, 256 MB  x740 D Chocolate standard input/

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i

Codeforces Round #424 (Div. 2) D. Office Keys(dp)

题目链接:Codeforces Round #424 (Div. 2) D. Office Keys 题意: 在一条轴上有n个人,和m个钥匙,门在s位置. 现在每个人走单位距离需要单位时间. 每个钥匙只能被一个人拿. 求全部的人拿到钥匙并且走到门的最短时间. 题解: 显然没有交叉的情况,因为如果交叉的话可能不是最优解. 然后考虑dp[i][j]表示第i个人拿了第j把钥匙,然后 dp[i][j]=max(val(i,j),min(dp[i-1][i-1~j]))   val(i,j)表示第i个人拿

Codeforces Round #424 (Div. 2) C. Jury Marks(乱搞)

题目链接:Codeforces Round #424 (Div. 2) C. Jury Marks 题意: 给你一个有n个数序列,现在让你确定一个x,使得x通过挨着加这个序列的每一个数能出现所有给出的k个数. 问合法的x有多少个.题目保证这k个数完全不同. 题解: 显然,要将这n个数求一下前缀和,并且排一下序,这样,能出现的数就可以表示为x+a,x+b,x+c了. 这里 x+a,x+b,x+c是递增的.这里我把这个序列叫做A序列 然后对于给出的k个数,我们也排一下序,这里我把它叫做B序列,如果我