[区间记忆化dp入门][Bribe the Prisoners SPOJ - GCJ1C09C][Codeforces Round #505D (rated, Div. 1 + Div. 2, ba]

Bribe the Prisoners SPOJ - GCJ1C09C

作为这类题代表,f[i][j]代表第i点到第j点单独处理的最值

这题关键:释放某个囚犯后,就把囚犯分成两段,两段互相独立 

这类dp问题的突破口在于一个点能把问题分成两段,两段互相独立,这两段的限制条件只与l-1,r+1有关,记忆化搜索,时间复杂度o(n*n*n)

#include<bits/stdc++.h>
using namespace std;
long long n,t,T,min1,m,a[100000],f[1000][1000];
long long dfs(int l,int r)
  { // cout<<l<<' '<<r<<endl;
     if (r<l) return 0;
     if (f[l][r])  return f[l][r];
     long long p=1e15;
     for (int i=l;i<=r;i++)
     p=min(p,(a[r+1]-a[l-1]-2)+dfs(l,i-1)+dfs(i+1,r));
    // cout<<l<<' '<<r<<' '<<p<<endl;
	 f[l][r]=p; 
     return p;  
  }
int main()
{  cin>>T;
  while (T--)
  {   t++;
    memset(f,0,sizeof(f));
     cin>>n>>m;
     min1=1e15;
     a[m+1]=0; a[m+2]=n+1;
     for (int i=1;i<=m;i++)  cin>>a[i];
     m=m+2;
     sort(a+1,a+1+m);
     for (int i=2;i<=m-1;i++)
	  min1=min(min1,(n-1)+dfs(2,i-1)+dfs(i+1,m-1));
	 cout<<"Case #"<<t<<": "; 
	  cout<<min1<<endl;  
  }
} 

 [Codeforces Round #505D (rated, Div. 1 + Div. 2, ba]

其实是一道很简单的区间dp记忆化搜索,太久没写这种类型的了。

定义二叉排序树

二叉排序树或者是一棵空树,或者是具有下列性质的二叉树

(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;

(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;

(3)左、右子树也分别为二叉排序树;

显然会想到排序, 预处理两点之间是否有>1的gcd

考虑任何一个点作为根节点,只要两边分别满足条件即可,(同时两边有一个树与根节点不互质,这个只要在找子区间根节点是特判就行)(两边互相独立不影响)  

节点两边分别为一段连续区间(且这段区间的根节点一定是r+1或者l-1

f[i][j][0/1]代表i-j区间是否可能作为根节点的左子树/右子树

#include<bits/stdc++.h>
using namespace std;
int  n,t,a[1000],g[1000][1000],f[1000][1000][2],v[1000][1000][2];
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
inline int gcd(int a,int b) {return b?gcd(b,a%b):a;}
int calc(int l,int r,int c)
{if (c==0)  return r+1;  else return l-1;}  
int dfs(int l,int r,int c)
{ if (l>r) return 1;
  if (v[l][r][c])  return f[l][r][c];
//注意因为f[l][r][c]可能处理过后值为0;所以用v[i][j][r]判断是否处理过(调好久原因)
  v[l][r][c]=1;
  int p=0;
  for (int  i=l;i<=r;i++)
  if (g[i][calc(l,r,c)] && dfs(l,i-1,0) && dfs(i+1,r,1))
  {p=1; break;} 
  f[l][r][c]=p; 
  return p;
}
int main()
{  n=read();
   for (int i=1;i<=n;i++)  a[i]=read();
   sort(a+1,a+1+n);
   for (int i=1;i<=n;i++)
     for (int j=1;j<=n;j++)
     if (gcd(a[i],a[j])>1) g[i][j]=1; else g[i][j]=0;
   for (int i=1;i<=n;i++)
   if (dfs(1,i-1,0)  && dfs(i+1,n,1)) {t=1;break;}
   if (t==1)  cout<<"Yes"<<endl;else cout<<"No"<<endl;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值