P4170 [CQOI2007]涂色 题解

CSDN同步

原题链接

简要题意:

一开始你有一个长度为 \(n\) 的无色串,每次可以对一个区间染上相同的颜色。问最少多少次可以形成目标串。

告诉你,这题的蓝是假的,太假了,数据也太弱了。

完全是个暴力选手乱碾标算用的,其实本题的时间复杂度完全可以做到 \(O(n^3)\).

第一眼看数据范围还以为是大力爆搜剪枝

\(f_{i,j}\) 表示 \([i,j]\) 区间形成目标串中 \([i,j]\) 的最少次数。

如果 \(s_i = s_j\),说明我们可以有两种情况:

  1. 反正这俩相等,那么 \(f_{i,j} = f_{i+1,j}\).

  2. 不然呢就是 \(f_{i,j} = f_{i,j-1}\).

即:

\[f_{i,j} = \min(f_{i+1,j} , f_{i,j-1}) \]

即从区间两端往内减少 \(1\).

如果不相等,我们需要枚举中间数 \(k\),即把区间一分两段。

\[f_{i,j}=\min(f_{i,k} + f_{k+1,j}) (i \leq k < j) \]

当然为了满足无后效性,我们需要先枚举长度(即按照区间长度进行计算,而不是按照左右端点)

此时我们兴致勃勃的写了一个程序。

时间复杂度:\(O(n^2)\).

实际得分:\(60pts\).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
 
int n,f[101][101];
string s;

int main(){
	cin>>s; int n=s.size(); 
	memset(f,0x3f,sizeof(f));
	for(int len=1;len<=n;len++)
	for(int i=1;i<=n-len+1;i++) { //左
		int j=i+len-1; //右
		if(len==1) f[i][j]=1; //长度为1
		else if(s[i]==s[j]) f[i][j]=min(f[i+1][j],f[i][j-1]);
			else for(int k=i;k<j;k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
	} printf("%d\n",f[1][n]);
	return 0;
}

为什么会 \(\text{WA}\) 呢?你也许不太明白。

那你就别明白了,背代码万岁

本人发现,洛谷上置于楼顶的几篇题解都是状态转移方程里好好的 \(s_i = s_j\),然后到代码里就是 \(s_{i-1} = s_{j-1}\),让人难以明白。这里特此说明!而且,如果有 \(s_i = s_j\) 也大多是下标从 \(0\) 开始枚举的。

然后你测了一下样例!

color 1.in
AAAAA

color 1.out
1

color.out
2

???你不明白自己的程序为什么会输出 \(2\)???

然后你开始调试,输出所有区间的值,得到一个结果是:

1 1 1
1 2 1
1 3 1
1 4 1
1 5 2
2 2 1
2 3 1
2 4 1
2 5 2
3 3 1
3 4 1
3 5 2
4 4 1
4 5 2
5 5 1

为什么所有以 \(5\) 结尾的区间(除了元区间 \([5,5]\)) 都有了错误的答案?

这就是因为,字符串下标从 \(0\) 开始 !!!

你访问 \(s_5\) 是一个空字符!

真是一个大坑啊

时间复杂度:\(O(n^3)\).

实际得分:\(100pts\).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
 
int n,f[101][101];
string s;

int main(){
	cin>>s; int n=s.size(); 
	memset(f,0x3f,sizeof(f));
	for(int len=1;len<=n;len++)
	for(int i=1;i<=n-len+1;i++) {
		int j=i+len-1;
		if(len==1) f[i][j]=1;
		else if(s[i-1]==s[j-1]) f[i][j]=min(f[i+1][j],f[i][j-1]);
			else for(int k=i;k<j;k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
	} printf("%d\n",f[1][n]);
	return 0;
}

posted @ 2020-03-31 19:34  bifanwen  阅读(149)  评论(0编辑  收藏  举报