Codeforces Global Round 6


Codeforces Global Round 6


A. Competitive Programmer

1)题目大意:

给一串数字,请将他们重新排列产生一个可以有前导零的数字。问有没有可能排列出一个60的倍数,有可能输出red。

2)思路:

太久没打了这题目一上来我居然写了个搜索

60可以分成3,2,10。也就是说必须有0,必须是3和2的倍数。

在该数是3的倍数的情况下,要么有至少一个偶数和至少一个0,要么至少有两个0 。

3)代码:
string s ;

void solve(){
	cin >> s ;
	ll nume = 0 , num0 = 0 ;
	ll sum = 0 ;
	for(char c : s){
		sum += c - '0' ;
		if(c == '0') num0 ++ ;
		if((c - '0') % 2 == 0) nume ++ ;
	}

	if(sum % 3 == 0 && (num0 >= 1 && nume >= 2 || num0 >= 2)) cout << "red\n" ;
	else cout << "cyan\n" ;
	
	
}

4)扩展:

倍数的性质,这题是60 。那么可以思考一下其他数字的倍数的性质

B. Dice Tower

1)题目大意:

有无穷个骰子,将他们垒起来,最底层和接触面的点数不算,算表面数字和。给若干个数字,问该数字有没有可能出现。

2)思路:

骰子的特点是对面两个点数和是7 。就是说每一层侧面点数和是2*7 。那么将所给数字模上14就能得出最顶层的数字了。若该数字是1~6中的一个那就ok 。当然,要注意题中会给小于十五的数字,这连一颗骰子都没有,需要特判。

3)代码:
void solve(){

	ll x ;
	cin >> x ;
	if(x % 14 >= 1 && x % 14 <= 6 && x >= 15) cout << "YES\n" ;
	else cout << "NO\n" ;
	
}

C. Diverse Matrix

1)题目大意:

给出一个r*c的矩阵,将该矩阵的每一列每一行的最大公约数求出放到一个长度为r+c的数组中,每一个最大公约数不相同,需要使得所有gcd的最大值最小。

2)思路:

真的是无语,为什么一个数会有gcd啊...

行gcd从1到r,列gcd从r+1到r+c构造就行 。特判一下一列的情况。

3)代码:
void solve(){

	cin >> n >> m ;
	if(n == 1 && m == 1) {
		cout << 0 ; return ;
	}
	
	if(m == 1){
		for(int i = 1 ; i <= n ; i ++ ) 
			cout << i + 1 << "\n" ;
		return ;
	}
	for(int i = 1 ; i <= n ; i ++ )
	for(int j = n + 1 ; j <= n + m ; j ++ )a[i][j - n] = i * j ;
	
	for(int i = 1 ; i <= n ; i ++ , cout << "\n")
	for(int j = 1 ; j <= m ; j ++ ) cout << a[i][j] << " " ;

}

D. Decreasing Debts

1)题目大意:

有m条借钱关系,(u,v,w)表示v借给uw元钱。现在需要我们简化这些关系使得总流水最小(总流水就是所有借钱关系的金额和)

2)思路:

我一开始真的以为是图论题目,以为有什么奇怪的算法可以解这个。结果一看题解,人麻了。要点就是收支平衡。把每个人的钱数量算好,然后排个序,欠钱最多的人还钱给借出最多的人,用双指针做一下。

3)代码:
ll n , m , num ; 
struct prsn {
	ll w , idx ;
}a[N] ;

struct{
	ll u , v , w ;
}ans[N] ;

bool cmp(prsn u , prsn v){
	return u.w < v.w ;
}

void solve(){

	cin >> n >> m ;
	
	for(int i = 1 ; i <= m ; i ++ ){//算好每个人的钱,负数表示欠钱
		
		ll u , v , money ;
		cin >> u >> v >> money ;
		if(u == v) continue ;
		a[u].w -= money ;
		a[v].w += money ;
		
	}
	
	for(int i = 1 ; i <= n ; i ++ )a[i].idx = i ;
	//debug ;
	sort(a + 1 , a + n + 1 , cmp) ;
	
	ll i = 1 , j = n ;
	
	while(i < j){
		
		while(i < j && a[i].w == 0) i ++ ;
		while(i < j && a[j].w == 0) j -- ;//找到第一个有欠钱关系的负债人和债主
		if(i >= j) break ;
		
		ll d = min(abs(a[i].w) , a[j].w) ;
		a[i].w += d ;
		a[j].w -= d ;
		
		ans[++ num].u = a[i].idx ;
		ans[num].v = a[j].idx ;
		ans[num].w = d ;
	}
	cout << num << "\n" ;
	for(int i = 1 ; i <= num ; i ++ ) 
		cout << ans[i].u << " " << ans[i].v << " " << ans[i].w << "\n" ;
}

E. Spaceship Solitaire

1)题目大意:

有n个东西,做一个需要一次操作。需要做完goal[i]个东西才算完成任务。现在给m个成就(u,v,w)表示做了v个u就能免费获得一个w。问在有1~i个成就的时候完成任务分别需要最少几次操作。

2)思路:

说实话这题不太明白。但是这和dp有点相似。我们要算第i次的最优,那就是从i-1转移过来的。而且这道题需要倒过来思考。我们需要完成i属于1到n的每一个goal[i],就是说在一个成就都没有的时候我们需要ans = sum(goal[i])次操作。那如果有了一个成就送我们一个x物品,那我们x物品就可以少做一个,操作数就少一次。当然,如果我们已经完成了goal[x]他再送,我们可以拿,但是不影响操作数。这题比较抽象,很有意思

3)代码:
ll n , m , ans ;
ll goal[N] , rwd[N] ;//reward

map > mp ;

void solve(){
	
	cin >> n ;
	get1(goal , n) ;
	for(int i = 1 ; i <= n ; i ++ ) ans += goal[i] ;
	cin >> m ;
	while(m -- ){
		ll u , v , w ;
		cin >> u >> v >> w ;
		
		if(!mp[u][v]){//若该成就没有出现过
			mp[u][v] = w ;
			rwd[w] ++ ;
			if(rwd[w] <= goal[w]){
				//该任务奖励有用 
				ans -- ;//就可以少做一次该任务奖励 
				
			} 
		}else{//已经出现过该任务
			
			rwd[mp[u][v]] -- ;
			
			if(rwd[mp[u][v]] < goal[mp[u][v]]) ans ++ ; 
			
			mp[u][v] = w ;//任务顶掉
			rwd[w] ++ ;
			if(rwd[w] <= goal[w]){
				
				ans -- ;
			}
		}
		cout << ans << "\n" ;
		
	}
	
}
4)拓展:

这种多状态输出的题目很大概率是每阶段最优一点一点转移过来的。和dp很像。

一些唠叨:

ACMer生涯过去了一年多,大学生活过去 了一年多,这一年里我很迷茫。时间像火车一样毫不留情地走着,而我就像穿着尿布的婴儿在月台上,在茫茫人海里呆呆地站着。我不知道要去哪里,我不知道要去找谁。值得庆幸的是,我是一个幸运的傻瓜,我遇到了一起打acm的伙伴们,我遇到了朱老师,遇到了阿伟老师。他们帮我拨开了遮盖天空的云雾,让我看到我想去的地方,让我知道我需要做什么,更重要的是,让我想要去做什么。真的无比感谢...