總結——關於2017 10 17測試的分析總結

本文解析了NOIP2017模拟赛三道题目的正解思路及代码实现:T1通过枚举时间和差分求解;T2使用双向链表优化子串操作;T3采用区间DP解决球消除问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NOIP 2017 模拟 :


----10.17




T1 : chair


题目 :  

******************************************************************************************************************

******************************************************************************************************************

*****************************************************和谐********************************************************

******************************************************************************************************************

******************************************************************************************************************

数据范围 :

95% :  n <= 10000;

100% :  n <= 100000; 1 <= x <= 100; 1 <= a, c < 24; 0 <= b, d < 60.


——正解思路 :

枚举时间, 差分, 求个前缀和;

——我的乱搞 :

排序, 枚举每个人, 求最大值.



——正解代码 :

#pragma GCC optimize("O3")
#include<cstdio>
int a[1441];
inline int read() {
	int i=0;
	char c=getchar();
	while(c<'0' or c>'9') c=getchar();
	while(c>='0' and c<='9') i = (i << 3) + (i << 1) + c - 48, c = getchar();
	return i;
}
int main()
{
	int n,x,h,m,ans=0;
	n=read();
	while(n--) {
		x=read();
		h=read();
		m=read();
		a[h*60+m]+=x;
		h=read();
		m=read();
		a[h*60+m]-=x;
	}
	x=0;
	for(int i=0;i<=1440;++i) {
		x+=a[i];
		if(x>ans) ans=x;
	}
	printf("%d\n",ans);
	return 0;
}






T2 : sort


题目 :  

******************************************************************************************************************

******************************************************************************************************************

*****************************************************和谐********************************************************

******************************************************************************************************************

******************************************************************************************************************



数据范围 :  

40% :  n <= 1000

70% :  n <= 50000

100% :  t <= 5;  n <= 100000.


——正解思路 :

用双向链表把多个子串连接起来, 连接子串内的数字. 每次O(1)删除不和谐的数, 然后依次检查合并或者删除子串.  代码细节较多. 复杂度 O(n).

——我的乱搞 :

寻找降序的子串, 并合并, 然后再反复统计, 剩余子串. ...然后光荣WA .

tips :

对于一些没有太大把握的题, 在写出 疑(jue)似(dui)正(bao)解(li) 的同时, 最好还是将数据分类处理, 将小数据用暴力解决, 防止爆 0. 




——正解代码 :

#pragma GCC optimize("O3")

#include <cstdio>
#include <cctype>
#include <list>

using namespace std;

list <list <int> > x;
list <list <int> > :: iterator itr, itr3;
list <int> :: iterator itr2;

inline int Read () {
	int i = 0;
	char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) i = (i << 3) + (i << 1) + c - 48, c = getchar();
	return i;
}

inline void put (int x) {
	short num = 0;
	char c[12];
	do c[++num] = x % 10 + 48, x /= 10; while (x);
	while (num) putchar(c[num--]);
}

int main () {
	int t, n, v, last;
	t = Read();
	while (t--) {
		x.clear();
		n = Read();
		last = -1;
		list <int > now;
		for (int i = 1; i <= n; i++) {
			v = Read();
			if (v < last) x.push_back(now), now.clear();
			last = v;
			now.push_back(v);
		}
		if (!now.empty()) x.push_back(now);
		while (x.size() >= 2) {
			for (itr = x.begin(); itr != x.end(); itr++) {
				if (itr != x.begin()) (*itr).pop_front();
				itr3 = itr;
				++itr3;
				if (!(*itr).empty() and itr3 != x.end()) (*itr).pop_back();
			}
			while (!x.empty() and x.begin() -> size() == 0) x.pop_front();
			for (itr = x.begin(); itr != x.end(); itr++)
				while ((++(itr3 = itr)) != x.end()) {
					if ((*itr3).empty() or (*itr3).front() >= (*itr).back()) {
						(*itr).splice((*itr).end(), (*itr3));
						x.erase(itr3);
					}
					else
						break;
				}
		}
		if (!x.empty()) {
			put(x.begin() -> size());
			putchar ('\n');
			for (itr2 = x.begin() -> begin(); itr2 != x.begin() -> end(); itr2++)
				put(*itr2),	putchar(' ');
		}
		else puts("0");
		putchar('\n');
	}
	return 0;
}






T3 : beans


题目 :  

******************************************************************************************************************

******************************************************************************************************************

*****************************************************和谐********************************************************

******************************************************************************************************************

******************************************************************************************************************


数据范围 : 

30% : n <= 9;

70% : t <= 1;

100% : t <= 10; n <= 200.


——正解思路 :

区间dp, 首先由于颜色相同的色块一定是一起消除的, 所以先把连在一起的点合成一个点, 同时记录每个点的点权. 

dp[ l ][ r ] 表示[ l , r ] 的球全部消除的最少次数.  num[ i ]表示点 i 的点权. 转移有以下四种 : 

3 - num[ l ] l == r

dp[ l ][ k ] + dp[ k + 1 ][ r ] l <= k < r

dp[ l + 1 ][ k - 1 ] + dp[ k + 1 ][ r - 1 ] color[ l ] == color[ r ] == color[ k ] and num[ l ] + num[ r ] <> 4 and num[ k ] == 1

dp[ l + 1 ][ r - 1 ] + max ( 0, 3 - num[ l ] - num[ r ])

所以我们就得到一个O( n ^ 3) 的 dp, 可以通过此题.

——我的乱搞 : 

暴力枚举每种情况,  伪剪枝. 

碰上较难的题......就要搞好暴力, 附某位 da lao 的A* 暴力 :


骗分骗样例, 暴力出奇迹.


——正解代码 :

var
n, t, i, j, k, cnt : integer;
ch, num : array [0..210] of integer;
dp : array [0..210, 0..210] of integer;
c : char;
s : string;
function max (a, b : integer) : integer;
begin
        if a > b then exit (a)
        else exit (b);
end;
function min (a, b : integer) : integer;
begin
        if a < b then exit (a)
        else exit (b);
end;
begin
        readln (t);
        while t > 0 do begin
                dec (t);
                fillchar (dp, sizeof (dp), $3f);
                fillchar (num, sizeof (num), 0);
                n := 0;
                s := '';
                read (c);
                while (c < '0') or (c > '1') do read (c);
                while (c = '0') or (c = '1') do begin
                        inc (n);
                        s := s + c;
                        if eoln then break;
                        read (c);
                end;
                cnt := 0;
                for i := 1 to n do
                        if s[i] <> s[i - 1] then begin
                                inc (cnt);
                                if s[i] = '0' then ch[cnt] := 0
                                else ch[cnt] := 1;
                                num[cnt] := 1;
                        end
                        else inc (num[cnt]);
                for i := cnt downto 1 do
                        for j := 1 to cnt do begin
                                if i = j then begin
                                        dp[i][j] := 3 - num[i];
                                        continue;
                                end;
                                if ch[i] = ch[j] then begin
                                        dp[i][j] := dp[i + 1][j - 1] + max(0, 3 - num[i] - num[j]);
                                        if num[i] + num[j] < 4 then begin
                                                k := i + 2;
                                                while k < j do begin
                                                        if num[k] = 1 then
                                                                dp[i][j] := min (dp[i][j], dp[i + 1][k - 1] + dp[k + 1][j - 1]);
                                                        inc (k, 2);
                                                end;
                                        end;
                                end;
                                for k := i to j - 1 do
                                        dp[i][j] := min (dp[i][j], dp[i][k] + dp[k + 1][j]);
                        end;
                writeln (dp[1][cnt]);
        end;
end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值