Codeforces Round #245 (Div. 2)

A. Points and Segments (easy)

http://codeforces.com/contest/430/problem/A


题目意思:给你n个点( n <= 100 ) 以及 m 个区间 ( m <= 100 ) , n个点分布在x轴上,你可以给每个点涂上红色或者是蓝色,但是要求那m个区间中,每个区间中的红色点和蓝色点的差的绝对值不能超过1。给你n个点的坐标,你要输出一个方案给这n个点涂上颜色


思路:其实想到就很简单了,如果这n个点是按顺序排好的,那么我们只要红蓝红蓝...这样一直涂 ,那么任意一个区间中红色和蓝色点的绝对值都不会超过1。不过题目中给的n个点的坐标不是按顺序的,所以排个序,然后再排回来即可


#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

#define MAXN 105

int n , m ;

struct Point{
	int x , col , id ;
}a[MAXN] ;

bool cmpbyid( Point a, Point b ) {
	return a.id < b.id ;
}

bool cmpbyx( Point a , Point b ) {
	return a.x < b.x ;
}

int main(){
	while( scanf( "%d%d" , &n , &m ) != EOF ) {
		for( int i = 1 ; i <= n ; i ++ ) {
			scanf( "%d%" , &a[i].x ) ;
			a[i].id = i ;
		}
		while( m -- ) {
			int l , r ;
			scanf( "%d%d" , &l , &r ) ;
		}
		sort( a + 1 , a + 1 + n , cmpbyx ) ;
		for( int i = 1 ; i <= n ; i ++ ) {
			a[i].col = a[i-1].col ^ 1 ;
		}
		sort( a + 1 ,  a + 1 + n , cmpbyid ) ;
		for( int i = 1 ; i <= n; i ++ )
			printf( i == n ? "%d\n":"%d " , a[i].col ) ;
	}
	return 0 ;
}

B. Balls Game

http://codeforces.com/contest/430/problem/B


题目意思 : 就是模拟一个祖玛游戏 , n( n <= 100 ) 个有颜色的小球排列成一排,并且保证刚开始不会有连续三个或者以上相同颜色的小球。然后你现在有一个颜色为x的小球,需要让你求把这个颜色为x的小球发射出去,最多能消掉多少球( 发射的小球不算 ) .


题目思路 :n小于等于100 ,直接模拟就可以了。输入的时候把相同颜色的球合并到一起会好敲一些


#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

int col[105] ;
int cnt[105] ;
int Index ;
int n , k , x ;

int cal( int id ) {
    int ans = 0 ;
    if( cnt[id] >= 2 ) {
        ans += 2 ;
    }else{
        return 0 ;
    }
    int l = id - 1 , r = id + 1 ;
    while( l > 0 && r <= Index && col[l] == col[r] && cnt[l] + cnt[r] >= 3 ) {
        ans += cnt[l] + cnt[r] ;l -- ; r ++ ;
    }
    return ans ;
}

int main(){
    
    while( scanf( "%d%d%d" , &n , &k , &x ) != EOF ) {
        Index = 0 ;
        for( int i = 1 ; i <= n ; i ++ ) {
            int c ; scanf( "%d" , &c ) ;
            if( c != col[Index] ) {
                Index ++ ;
                col[Index] = c ;
                cnt[Index] = 1 ;
            }else{
                cnt[Index] ++ ;
            }
        }
        int Max = 0 ;
        for( int i = 1 ; i <= Index ; i ++ ) {
            if( col[i] == x ) {
                Max = max( Max , cal( i ) );
            }
        }
        printf( "%d\n" , Max ) ;
    }
    return 0 ;
}

C. Xor-tree

http://codeforces.com/contest/430/problem/C


题目意思: 有一棵有根树,根是node 1 ,每个结点都有一个值 0 或者 1 , 你有一个操作 , 就是选择一个点 , 将这个点取反 , 他的儿子结点保持不变 , 他的孙子结点取反 , 儿子的孙子保持不变 ... and so on . ( 看清楚题意 , 这里的影响是隔代的 ... 刚开始读错题了 , 样例还刚好能出 , 结果错的惨兮兮的 ) 。 然后给你每个结点的目标状态,问你需要最少的操作次数使初始状态变到目标状态 ,并且把你改变了哪些结点也输出来 。


思路 :首先要明确的一点是,每个结点肯定最多肯定只用变一次( 因为变两次和不变结果是一样的 ) , 那么我们只需要从根节点开始推就行了。如果根节点的初始状态和目标状态不同,那么根节点必须要变,因为没有其他的结点能影响他了,反之就是肯定不变。那么他的孩子结点呢?因为根节点的操作已经定死了,所以他的孩子结点也只能根据根节点的操作去选择要不要改变,否则后面也没有结点可以影响他了...and so on . 那么其实每个结点是是否操作都已经定死了,我们只要递归到某个节点的时候根据他祖宗结点的操作来决定要不要改变就可以了。因为我们要知道他的祖宗结点的操作数,所以我们dfs的时候需要维护两个值,一个是跟自己相差奇数代的祖宗有多少个结点做了操作,还有一个就是隔了偶数代的。这个就可以在O(n)的时间复杂度下解决这个问题了。


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;

#define MAXN 100005

struct Tree{
	struct Edge{
		int to , nex ;
		Edge(){}
		Edge( int _to , int _nex ) :to(_to),nex(_nex){}
	}edge[MAXN*2] ;
	int head[MAXN] ;
	int Index ;

	void init(){
		Index = 0 ;
		memset( head , -1 , sizeof(head) ) ;
	}

	void add( int from , int to ) {
		edge[Index] = Edge( to , head[from] ) ;
		head[from] = Index ++ ;
	}
}T;

int val[MAXN] ;
int goal[MAXN] ;
vector<int> ans ;

void dfs( int u , int p , int cnt1 , int cnt2 ) {
	int change = 0 ;
	if( (val[u] + cnt2 ) % 2 != goal[u] ) {
		change = 1 ;
		ans.push_back( u ) ;
	}
	for( int i = T.head[u] ; ~i ; i = T.edge[i].nex ) {
		int ch = T.edge[i].to ;
		if( ch == p ) continue ;
		dfs( ch , u , cnt2 + change , cnt1  ) ;
	}
}

int main(){
	int n ;
	while( scanf( "%d" , &n ) != EOF ) {
		T.init() ;
		for( int i = 1 ; i < n ; i ++ ) {
			int u , v ;
			scanf( "%d%d" , &u , &v ) ;
			T.add( u , v ) ;
			T.add( v , u ) ;
		}
		ans.clear() ;
		for( int i = 1 ; i <= n ;i  ++ ) 
			scanf( "%d" , &val[i] ) ;
		for( int i = 1 ; i <= n ; i ++ )
			scanf( "%d" , &goal[i] ) ;
		dfs( 1 , -1 , 0 , 0 ) ;
		printf( "%d\n" , ans.size() ) ;
		sort( ans.begin() , ans.end() ) ;
		for( int i = 0 ; i < ans.size() ; i ++ ) {
			printf( "%d\n" , ans[i] ) ;
		}
	}
	return 0 ;
}

D. Working out

http://codeforces.com/contest/430/problem/D


题目意思: 有一个n*m的矩阵 , ( 3 <= n , m <= 1000 ) , 矩阵中的每个点都有权值 , 现在有两个人, 一个人要从左上角走到右下角 , 另一个要从左下角走到右上角 ( 两个人走的速度可以不一样, 也就是所两个人走过的格子数可以不一样 )  , 要求两个人所走的路径只能有一个交点 , 求两个人所走路径的权值和最大( 交点不计算在内 ) 。

思路 : 我们假设两个人的交点是( x , y ) , 那么这两个人的行走路径有两种情况 :

1. 从左上角到右下角的人 , 从(1,1)-> ( x-1,y ) ->( x , y ) -> ( x + 1 , y ) -> ( n , m ) , 而与此同时从左下角走到右上角的人就必须是 ( n , 1 ) -> ( x , y -1 ) ->( x , y ) ->( x , y + 1 ) -> ( 1 , m )

2.从左上角到右下角的人, 从( 1, 1 )  -> ( x , y - 1 ) ->( x, y ) -> ( x , y +1 ) ->( n , m ) , 而与此同时从左下角走到右上角的人就必须是( n , 1) -> ( x + 1 , y ) -> ( x , y ) ->( x - 1 , y ) -> ( 1 , m )

并且要满足 x >= 2 && x < n && y >= 2 && y < m , 其他情况两个人的路径都会至少有两个交点。

那么知道了这两种情况, 那么问题就好解决了,直接开四个数组,求从四个角到( i , j ) 的路径最大权值和 , 然后枚举交点就可以了


#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

#define MAXN 1005

int n , m ;
int a[MAXN][MAXN] ;

int dpLU[MAXN][MAXN] , dpRU[MAXN][MAXN] ;
int dpLD[MAXN][MAXN] , dpRD[MAXN][MAXN] ;

int main(){
    while( scanf( "%d%d" , &n , &m ) != EOF ) {
        for( int i = 1 ; i <= n ; i ++ ) {
            for( int j = 1 ; j <= m ; j ++ ) {
                scanf( "%d" , &a[i][j] ) ;
            }
        }
        for( int i = 1 ; i <= n ; i ++ ) {
            for( int j = 1 ; j <= m ; j ++ ) {
                dpLU[i][j] = max( dpLU[i][j-1] , dpLU[i-1][j] ) + a[i][j] ;
            }
        }
        for( int i = 1 ; i <= n; i ++ ) {
            for( int j =  m ; j >= 1 ; j -- ) {
                dpRU[i][j] = max( dpRU[i][j+1] , dpRU[i-1][j] ) + a[i][j] ;
            }
        }
        for( int i = n ; i >= 1 ; i -- ) {
            for( int j = 1 ; j <= m ; j ++ ) {
                dpLD[i][j] = max( dpLD[i+1][j] , dpLD[i][j-1] ) + a[i][j] ;
            }
        }
        for( int i = n ; i >= 1 ; i -- ) {
            for( int j = m ; j >= 1 ; j -- ) {
                dpRD[i][j] = max( dpRD[i+1][j] , dpRD[i][j+1] ) + a[i][j] ;
            }
        }
        int Max = -1 ;
        for( int i = 2 ; i <= n - 1 ; i ++ ) {
            for( int j = 2 ; j <= m - 1 ; j ++ ) {
                Max = max( Max , dpLU[i][j-1] + dpRD[i][j+1] + dpLD[i+1][j] + dpRU[i-1][j] ) ;
                Max = max( Max , dpLU[i-1][j] + dpRD[i+1][j] + dpLD[i][j-1] + dpRU[i][j+1] ) ;
            }
        }
        printf( "%d\n" , Max ) ;
    }
    return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值