【区间dp】uva10003+ uva 1626 括号匹配问题 【有空自己记忆化写一下!!!】

版权声明:版权所有……啊重点是我简直找不到第二个人写比我还多的吐槽-。- https://blog.csdn.net/StrongerIrene/article/details/81944120

讲道理,其实我还不是太懂,这个题看到了两种写法

之前大概想的差不多,要这样实现呀:

常规写法,大概n……3

递归写法,稍微好理解一点

好了,接下来自从看了liuchuo的博客我要变身玛丽苏橙色了 

 题目链接 并不复杂,只是切木棍。。。 让我想到其实和以前的形式很像,这里给的是木棍最长1000,完全可以改到1000000000这种样子,但是我们存下来的是切点的位置,这种时候真的只要枚举一些切点就可以了

注意(1) 

根据题目的特殊性分析,如果是最小的那个区间(头和尾相差是1)是不用切的,然后就会返回一个0

每次切的时候用a[i]存下来之后,每次只要用到那个差值就好了a[j]-a[i]

真方便啊!!

注意(2)

a[0]=0 a[n+1]=len ,有点像是题目里的隐含条件,但是切的时候要从头开始切,切到最后的啊!!!

核心代码(哇这个颜色好好看啊柠檬一样的!!!)

(怎么我看了这么久没人把博客写的五颜六色啊!!!!!!!!!除了柳婼同学)


int DP(int i,int j)
{
    if(dp[i][j]!=-1)
        return dp[i][j];
    else if(j-i==1)
        return dp[i][j]=0;
    else
    {
        int t;
        dp[i][j]=INF;
        for(int k=i+1; k<j; k++)
        {
            t=DP(i,k)+DP(k,j)+a[j]-a[i];
            if(dp[i][j]>t)
                dp[i][j]=t;
        }
        return dp[i][j];
    }
}

哇! 我竟然10分钟A掉了,忽然开心!!!
下学期即便有大物也要好好敲代码啊!想再学好日语,顺便摸一个网工的证书过来哇嘎嘎嘎

//15:05  hope 10 minutes and I'll solve the I
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int len,n;
int a[55];
int d[55][55];
//remember too!!!!!!!!!!!!!
//-1 and 0 differs
int dp(int  i,int j){
	if(d[i][j]!=-1)return d[i][j];
	else if(i+1==j)return 0;
	else {int t=0x3f3f3f;
	//for every time of 枚举
	//啊,这个那个,因为我们知道1,1是没意义的,实际上1,2也是没意义的,
	//理论上完全可以化成0然后找意义啥的,所以没等号,有+1
		for(int k=i+1;k<j;k++){
			d[i][j]=dp(i,k)+dp(k,j)+a[j]-a[i];//因为分解开的是j和i这个状态,希望搞搞清楚
			t=min(t,d[i][j]);
		//d[i][j]=t;
		}
		d[i][j]=t;
		//记得最后要赋值了
	}
	return d[i][j];
}

int main(){
	while(cin>>len,len){//good ways for judging 0 remember!
		//yes!  remains at least a year here
		cin>>n;
		for(int i=1;i<=n;i++)cin>>a[i];
		a[0]=0;a[n+1]=len;
		memset(d,-1,sizeof(d));
		//two index can use it to
		///------------------

		cout<<"The minimum cutting is "<<dp(0,1+n)<<"."<<endl;
	}

return 0;
}

(下面的待补充,以后不是水题的用大号切或者交到洛谷(?))

括号匹配问题:

(1)n3的算法,可以化简,但不是现在

(2)我提出了一个大胆猜想:如果直接暴力做呢?

zj说会串的,比如([)]这样会判定为合法

再不济,( [ ) ] )也不能统计的

我们还是每个状态就都考虑一下比较靠谱

再说n是1000啊。。。

怎么就想不到要用区间dp来做呢。。

然后就是这里有一些数据,针对: ([()

在我最后看的那份代码里面,链接 新年抽大奖!点击试试手气(不)

做好初始化工作。和上面的不同的是-----

先枚举掉的是长度,按照每个长度会有怎么样的i和j  i是起点,j是终点,是+len得到的

随后再用O(n)的时间跑一遍,对于从i到j的这个串,从哪里切开是最好的,并且保存下来,这里要求min,就取min

反正都已经是匹配完成了的  就取最小的裂开形式哦呀  这样()[ ] 这不就这样断着匹配上了

因为最开始枚举过来的时候 () 1-2 大概就到了2 1  理所当然是0了。。 min被记住

如果是(()枚举的时候从1 3 到2 2 就是1

写法有很多。。。可以试试记忆化

枚举的话,先是枚举长度,长度小的时候,每个地方端点也很小,k那里只能枚举一次

长度很长的时候,0-4的只能枚举一个0-4的if else  之后k断点就多了,3个地方都可以分,取得一个最小的-。-

-。-   。。。


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define N 110
#define inf 0x3f3f3f3f

int dp[N][N];
char s[N];

int main()
{
	scanf("%s", s);
	int n = strlen(s);
	
	for (int i = 0; i<n; ++i) dp[i][i] = 1;
	for (int len = 1; len<n; ++len)
		for (int i = 0; i + len<n; ++i)
		{
			
			int j = i + len;
		//	printf("i %d len  %d   j  %d  \n", i, len, j);
			dp[i][j] = inf;
			if (s[i] == '('&&s[j] == ')' || s[i] == '['&&s[j] == ']')
				dp[i][j] = min(dp[i][j], dp[i + 1][j - 1]);
			else if (s[i] == '('&&s[j] != ')' || s[i] == '['&&s[j] != ']')
				dp[i][j] = min(dp[i][j], dp[i + 1][j] + 1);
			else if (s[i] != '('&&s[j] == ')' || s[i] != '['&&s[j] == ']')
				dp[i][j] = min(dp[i][j], dp[i][j - 1] + 1);
			//printf(" dp %d\n", dp[i][j]);
			for (int k = i; k < j; ++k) {
				dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
			//	printf("i %d  k %d  j  %d  dp %d\n",i, k,j,dp[i][j]);
			}//cout << "-----" << endl;
		}
	printf("%d\n", dp[0][n - 1]);
	return 0;
}

还有 ,这是codevs的代码,uva的还要路径, 我已经阵亡了

--------------------

以下为转载

最后一次把不合法的S变为合法的之前可能情况: 
1)S形如(S′)或[S′]: 
只需把S′变合法即可。 
f[i,j]= f[i+1,j-1] 
2)S形如(S′ 或[S′: 
先把S′变为合法的,右边加 )或]即可。 
f[i,j]= f[i+1,j]+1 
3)S形如 S′)或S′]: 
先把S′化为合法的,左边加(或 [即可。 
f[i,j]= f[i,j-1]+1 
4)把长度大于1的序列SiSi+1…..Sj-1Sj分为两部分: 
Si…… Sk,Sk+1….. Sj 
分别化为规则序列. 
则:f[i,j]=f[i,k]+f[k+1,j] ;i<=k<=j-1; 
上述4种情况取最小值即可。

---------------------------------------------------------------------

我们还没有解决路径输出问题:这里用到了很巧妙的方法,先常规算完了对于整个串 的,拿到一个结果,根据这个结果再算,

用print递归打印(0,n-1),如果匹配就( [             print (i,j)               ])

如果不匹配就() print 或者是print  ()

但是要煲粥保证最优,这里就更迷了,不管你那个结果是怎么形成的,只要我遍历的时候搞到他了,我就认为他是可行的

 if(dp[x][y]==dp[x][k]+dp[k+1][y]){
             print(x,k); print(k+1,y);
             return;  
 }

print里面还要有临街状态,,

 if(x>y) return;

emmmmm,接下来我要自己打了。(代码待补充)

还有一个问题就是gets,因为可能是空串。可谓是非常坑爹,可能一般人也想不到了

大概是说了at  most吧。。给数据的话就是0<=x<=100了,所以读入有坑。

写了个记忆化版本的,没有用fgets,也没输出路径


#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
int d[105][105];
int rec;
string s;
int dp(int i, int j) {
	//记忆化  一般会比递推快一点??
	if (d[i][j] != 0)return d[i][j];
	else if (i == j + 1)return 0;///  3 2
	else if (i == j)return 1;
	d[i][j] = 0x3f3f3f;
	if (s[i] == '('&&s[j] == ')' || s[i] == '['&&s[j] == ']')
		d[i][j] = min(d[i][j], dp(i + 1, j - 1));
	else  if (s[i] == '('&&s[j] != ')' || s[i] == '['&&s[j] != ']')
		d[i][j] = min(d[i][j], dp(i + 1, j) + 1);
	else  if (s[i] != '('&&s[j] == ')' || s[i] != '['&&s[j] == ']')
		d[i][j] = min(d[i][j], dp(i, j - 1) + 1);
	//这三种情况之后,还要分裂开讨论
	for (int k = i; k < j; k++) {
		d[i][j] = min(d[i][j], dp(i, k) + dp(k + 1, j));
	}
	//***************  k不能等于j,不然会stack overflow
	// 为什么呢? 1,2   的时候(1,2)+(3,2),反正会非法越界访问吧
	///d[i][j]先初始化成inf
	//k=i
	return d[i][j];
}


int main() {
	int t; cin >> t; while (t--) {
		cin >> s;
		//这两行格式不对 后面要改的
		int n = s.length();
		for (int i = 0; i <= n; i++)d[i][i] = 1;// one can't find another
												//dp[i+1][j]是0   at first we have known it...
		rec = dp(0, n - 1);
		cout << rec << endl;
		//print(0,n-1);
	}
	return 0;
}

然后。。。 我写了两天这个题。。。zj一直劝我记忆化搜索,记忆化搜多,后来T了。。。。

后来T了。。。。。。。。

后来T了。。。。。。。。

结果zj狂笑不止。。。。。。。。。。。。。

好吧,我感觉我已经没有精力了,下面是T的代码

需要注意

①    for (int k = i; k <j; k++) {
        d[i][j] = min(d[i][j], dp(i, k) + dp(k + 1, j));     }

这里是不能等于j的,不然就会(1,2)一直循环下去

(在求1,2值的时候跑到一个这样的k,k没结果,一直跑,栈堆得很高,最后就overflow了)

②d[i][j]=inf 不然求的时候会被0 min掉值

③ 记忆化搜索大部分情况下好一点,访问的状态少(?)

但这个题访问的状态很多,都要访问,T也很多,函数开销不可避免,就又很多栈,最后T了

.......

④fgets  会把最后那个空格读进去,所以要s.len-1  (n) 否则答案不对了。还有就是s要是char数组

fgets(s,999,stdin);

最后记得多个T一定要清空
 


#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
typedef long long ll;
int d[105][105];
int rec;
char s[105];
//string s;
int dp(int i, int j) {
	//记忆化  一般会比递推快一点??
	//cout << i << " " << j <<" "<<d[i][j]<< endl;
	if (d[i][j] != 0)return d[i][j];
	else if (i == j + 1)return 0;///  3 2
	else if (i == j)return 1;
	d[i][j] = 0x3f3f3f;
	if (s[i] == '('&&s[j] == ')' || s[i] == '['&&s[j] == ']')
		d[i][j] = min(d[i][j], dp(i + 1, j - 1));
	else  if (s[i] == '('&&s[j] != ')' || s[i] == '['&&s[j] != ']')
		d[i][j] = min(d[i][j], dp(i + 1, j) + 1);
	else  if (s[i] != '('&&s[j] == ')' || s[i] != '['&&s[j] == ']')
		d[i][j] = min(d[i][j], dp(i, j - 1) + 1);
	//这三种情况之后,还要分裂开讨论
	for (int k = i; k <j; k++) {
		d[i][j] = min(d[i][j], dp(i, k) + dp(k + 1, j));
	}
	//***************  k不能等于j,不然会stack overflow
	// 为什么呢? 1,2   的时候(1,2)+(3,2),反正会非法越界访问吧
	///d[i][j]先初始化成inf
	//k=i
	return d[i][j];
}



void print(int i, int j) {
	if (i > j) return;
	if (i == j) {
		if (s[i] == '(' || s[i] == ')') printf("()");
		else printf("[]");
		return;
	}
	int ans = d[i][j];
	if ((s[i] == '('&&s[j] == ')' || s[i] == '['&&s[j] == ']') && ans == d[i + 1][j - 1]) {
		printf("%c", s[i]); print(i + 1, j - 1); printf("%c", s[j]);
		return;
	}
	for (int k = i; k < j; k++)
		if (ans == d[i][k] + d[k + 1][j]) {
			print(i, k); print(k + 1, j);
			return;
		}
	//..... ???怪不得ans没报错。。。  我真是shadiao  里面也要递归啊
}


int main() {
	int t; cin >> t;
	getchar();
	while (t--) {
		//getchar();
		fgets(s, 105, stdin);
		//cin >> s;
		//cout << s << endl;
		// aba  如果后面是1  就读a  如果是999  就读一堆 到换行符结束
		//为了应对空格。。  这是c++11的新语法
		int n = strlen(s)-1;
		for (int i = 0; i < n; i++) {
			for (int j = i+1; j < n; j++) {
				d[i][j] = 0;
			}
		}
		for (int i = 0; i <= n; i++)d[i][i] = 1;// one can't find another
												//dp[i+1][j]是0   at first we have known it...
		rec = dp(0, n - 1);
		//cout << rec << endl;
		print(0,n-1);
		cout << endl;// << endl;
	}
	return 0;
}

wzj最帅

--------------------

(不准 的不准的。。)(下面是图片)

emmmmmmm。。。 我好想做bzoj-。-

刚刚看到他们初中就开始参加noip的好多人 ,而我大二下才开始搞这个东西,大二寒假sort才刚刚知道怎么用,我真是呜呜呜呜呜呜┭┮﹏┭┮  我已经20岁了,洛谷上到处都是小孩子……

 “ 算下来这已经是我参加noip的第四年了呢 ” 我&……%¥()

claris 3500题的bzoj-。-啊啊啊啊啊啊啊~

在洛谷上刷到300我觉得也已经就很厉害了……

或者是把抽特王的题单也写完……啊啊啊啊

一直做一件事情做不出来其实就挺有挫败感的…… 

但是摸着鱼做就……啊啊啊啊啊啊……  我们那里真是个小地方 我怎么就没早点知道这些东西呢……

猜你喜欢

转载自blog.csdn.net/StrongerIrene/article/details/81944120