[USACO17OPEN]Modern Art 2 现代艺术2

小TY的同学HF用2017年创作了一幅艺术作品,需要复制。给定一个长度为N的画条,含有不同颜色,每次只能用一种颜色填充连续区间,且必须等待1天颜料干透。求复制并全干的最少天数。若无法完成则输出-1。题解中提到了拓扑排序和栈的解决方案。

题目背景

小TY的同学HF也想创作艺术

HF只有一块长条状的画布(画条),所以每一次涂色只能涂上连续几个单位的颜料,同样新的颜料可以完全覆盖旧的颜料

由于他的颜料同样非常傲娇,每次涂完要等上1day才能完全干,只有旧颜料干了以后才能用新颜料覆盖

现在小HF用了2017个年头终于画出了一个大作品,自己非常满意

现在他想复制这份作品

题目描述

现在给你一个长度为N(N≤1e5)的画条

上面有若干种颜色,每位的数字表示一种颜色,0表示没有涂色

为了快捷,每次涂色可以用一种颜色填充一个区间,同一种颜色只能使用一次

每次可以涂色好几次,但是这些区间必须分别连续切两两不能相交

然后等待1day油漆干了后再同样操作,输出创作完成并全干了后的最少时间

输入输出格式

输入格式:

第一行为N,画条长度

一下N行每行一个数表示颜色

输出格式:

输出一个整数表示最少天数。数据若不合法则输出-1

输入输出样例

输入样例#1:

7
0
1
4
5
1
3
3

输出样例#1: 

2

说明

In this example, the interval of color 1 must be painted in an earlier round than the intervals of colors 4 and 5, so at least two rounds are needed.

样例解释:

第一次可以把1颜色和3颜色填充,变成

0 1 1 1 1 3 3

等待1Day后再填充颜色4和颜色5,变成

0 1 4 5 1 3 3

在等待一Day油漆干了后创作完成

所以答案是2

感谢 @ Night_Aurora 贡献翻译

 

方案1 : 拓扑排序

记录头和尾,然后理清楚每种颜色的关系,并对入度为0的点进行排序,感觉好复杂,虽然我一开始也是这样想的

这样首先会炸内存,时间也有可能会炸

方案2 : 栈

因为不管颜色混成怎么样的一坨,时间都是最高的层数,没毛病吧,所以我们就直接用栈了

首先记录当前的点是哪一个点的开头或者结尾,如果不是就为0

90代码

#include <iostream>
using namespace std ;

const int N = 1e5 + 10 ;
int n , a[N] ;
int s[N] , t[N] ; //s为当前点是哪一个点的开头,t表示当前点是那一个点的结尾
int x[N] , y[N] ;
int top , ans = 0 ; //top表示栈顶,ans表示最大值
int main() {
    cin >> n ;
    for ( int i = 1 ; i <= n ; i ++ ) {
        cin >> a[i] ; y[a[i]] = i ;
        if ( x[a[i]] == 0 ) x[a[i]] = i ;
    }
    for ( int i = 1 ; i <= n ; i ++ ) {
        if ( x[i] != 0 ) s[x[i]] = i ;
        if ( y[i] != 0 ) t[y[i]] = i ;
    }
    for ( int i = 1 ; i <= n ; i ++ ) { //枚举每一个点
        if ( a[i] == 0 ) { //如果中断了
            if ( top != 0 ) {  //如果栈里面还有颜色没有结束
                cout <<  "-1" << endl ;  //就直接放回-1
                return 0 ; 
            }
            continue ; 
        }
        if ( s[i] != 0 ) top ++ , ans = max ( ans , top ) ; //如果这是开头,就top++
        if ( t[i] != 0 ) top -- ; //否则就top-- , 注意一个点可能是开头和结尾
    }
    cout << ans << endl ;
    return 0 ;
}

这个方法很明显是不对的,能过90%的数据已经是个奇迹了

原因就是这个代码只判断了0打断颜色的情况,比较片面

比如说这个数据:

5
1
2
1
2
1

代码输出2

正确答案应该是-1

能过90分应该是因为数据很大,而且有多个出现错误的地方

所以我们还要再打一个栈的数组,不能只保留栈顶

100分代码

#include <iostream>
using namespace std ;

const int N = 1e5 + 10 ;
int n , a[N] ;
int s[N] , t[N] ;
int x[N] , y[N] ;
int top , sta[N] , ans = 0 ;
int main() {
	//freopen( "testdata.in","r",stdin ) ;
	cin >> n ;
	for ( int i = 1 ; i <= n ; i ++ ) {
		cin >> a[i] ;
		if ( a[i] == 0 ) continue ;
		 y[a[i]] = i ;
		if ( x[a[i]] == 0 ) x[a[i]] = i ;
	}
	for ( int i = 1 ; i <= n ; i ++ ) {
		if ( x[i] != 0 ) s[x[i]] = i ;
		if ( y[i] != 0 ) t[y[i]] = i ;
	}
	for ( int i = 1 ; i <= n ; i ++ ) {
		if ( s[i] != 0 ) sta[++top] = s[i] , ans = max ( ans , top ) ;
		if ( sta[top] != a[i] ) { cout << "-1" << endl ; return 0 ; } //如果当前的颜色不是最上面那层的颜色,说明数据有误 
		if ( t[i] != 0 ) top -- ; //到终点就出栈 
	}
	cout << ans << endl ; //输出 
	return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值