讲道理,其实我还不是太懂,这个题看到了两种写法
之前大概想的差不多,要这样实现呀:
好了,接下来自从看了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我觉得也已经就很厉害了……
或者是把抽特王的题单也写完……啊啊啊啊
一直做一件事情做不出来其实就挺有挫败感的……
但是摸着鱼做就……啊啊啊啊啊啊…… 我们那里真是个小地方 我怎么就没早点知道这些东西呢……