Bribe the Prisoners SPOJ - GCJ1C09C
作为这类题代表,f[i][j]代表第i点到第j点单独处理的最值
这题关键:释放某个囚犯后,就把囚犯分成两段,两段互相独立
这类dp问题的突破口在于一个点能把问题分成两段,两段互相独立,这两段的限制条件只与l-1,r+1有关,记忆化搜索,时间复杂度o(n*n*n)
#include
using namespace std;
long long n,t,T,min1,m,a[100000],f[1000][1000];
long long dfs(int l,int r)
{ // cout<>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 #"<
[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
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"<