20261001 DP专题
toasting
\(n\) 个人围成一圈,每个人有一权值 \(c_i\),将他们两两连线,求在线不交叉的情况下最多有多少对线两端的人的权值相等。
-
一种情况:\(l\) 和 \(r\) 连线,他们之间的最大配对数都能由他们之间的人,也就是 \(l+1\) 到 \(r-1\) 的人的配对数量得来。因为 \(l\) 和 \(r\) 连线之后必然不与其之间的任何线相交。具体来说,如果 \(l\) 和 \(r\) 的权值 \(c\) 不同,那么他们之间的最大配对数为 \(l+1\) 和 \(r-1\) 之间的最大配对数;否则若相等,就是 \(l+1\) 和 \(r-1\) 之间的最大配对数加一;
-
另一种情况:在 \(l\) 到 \(r\) 之间枚举割点 \(k\),考虑 \(l\) 到 \(k\) 互相配对,\(k+1\) 到 \(r\) 互相配对,\(l\) 到 \(r\) 区间的答案即为两边之和;
#include<bits/stdc++.h>
using namespace std;
int a[2003],dp[2003][2003];
int main(){
freopen("toasting.in","r",stdin);
freopen("toasting.out","w",stdout);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[n+i]=a[i];
if(n%2!=0){
printf("0\n");
return 0;
}
for(int i=1;i<=2*n;i++)
if(a[i]==a[i+1]) dp[i][i+1]=1;
for(int len=2;len<=n;len++)
for(int l=1;l<=n;l++){
int r=l+len-1;
dp[l][r]=dp[l+1][r-1]+(a[l]==a[r]);
for(int k=l+1;k<r;k+=2) dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r]);
}
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,dp[i][i+n-1]);
printf("%d\n",ans);
return 0;
}
strawberry
一棵有根树,节点有权值,判断有没有拆除任意两条边的方案,使得三部分的权值和相等。
记 \(k\) 为权值总和的三分之一。
-
一种情况:如果当前结点 \(i\) 的儿子中,有其中两个儿子 \(s1\),\(s2\),他们的子树中都有权值为 \(k\) 的节点,此时切掉s1和s2的父边为一个可行方案。
-
另一种情况:如果当前结点不是根,子树和恰好为 \(2*k\),并且其子树中存在权值为 \(k\) 的子树。此时切掉其父边,以及它某个后代的父边,形成一个可行的方案。
注意特判 \(k\) 不为整数和全零的情况。
#include<bits/stdc++.h>
using namespace std;
vector<int> tr[100003];
int val[100003],sum[100003],fa[100003],flag[100003];
bool tag=false;
void work(int u){
sum[u]=val[u];
for(int i:tr[u]){
if(i==fa[u]) continue;
work(i);
sum[u]+=sum[i];
}
return;
}
void check(int u,int pur,int ls){
if(sum[u]==2*pur) ls=1;
else if(sum[u]==pur){
flag[u]=1;
if(ls==1){
tag=true;
return;
}
}
int opt=0;
for(int i:tr[u]){
if(i==fa[u]) continue;
check(i,pur,ls);
if(flag[i]==1) opt++,flag[u]=1;
}
if(opt>=2) tag=true;
return;
}
int main(){
freopen("strawberry.in","r",stdin);
freopen("strawberry.out","w",stdout);
int t;
scanf("%d",&t);
while(t--){
memset(flag,0,sizeof(flag));
int n,root;
scanf("%d",&n);
for(int i=1;i<=n;i++) tr[i].clear();
for(int i=1;i<=n;i++){
scanf("%d%d",&fa[i],&val[i]);
if(fa[i]==0) root=i;
else tr[i].push_back(fa[i]),tr[fa[i]].push_back(i);
}
work(root);
tag=false;
check(root,sum[root]/3,0);
if(sum[root]==0){
if(n>=3) printf("YES\n");
else printf("NO\n");
}
else if(sum[root]%3!=0) printf("NO\n");
else if(tag==true) printf("YES\n");
else printf("NO\n");
}
return 0;
}
meaning
两个字符串 \(s\) 和 \(t\),其中 \(t\) 有两个意思,求 \(s\) 总共有多少种意思,\(t\) 不能重叠。
答案对 \(1e9+7\) 取模。
-
\(KMP\) 求 \(s\) 中所有 \(t\) 子串;
-
\(dp[i]\) 表示 \(s\) 前 \(i\) 位的意思数。若 \(t\) 与 \(s\) 匹配的结束位置为 \(i\),那么 \(dp[i]\) 为 \(dp[i-1]\) 加上 \(dp[i-lent]\);否则为 \(dp[i-1]\);
#include<bits/stdc++.h>
using namespace std;
int bor[100003],same[100003],dp[100003];
const int mod=1e9+7;
int main(){
freopen("meaning.in","r",stdin);
freopen("meaning.out","w",stdout);
int n;
cin>>n;
while(n--){
string s,t;
cin>>s>>t;
int lens=s.length(),lent=t.length();
s='*'+s,t='*'+t;
memset(bor,0,sizeof(bor));
memset(same,0,sizeof(same));
bor[1]=0;
int p=0;
for(int i=2;i<=lent;i++){
while(p>0&&t[i]!=t[p+1]) p=bor[p];
if(t[i]==t[p+1]) p++;
bor[i]=p;
}
int ids=1,idt=0,tot=0;
while(ids<=lens){
if(s[ids]==t[idt+1]) ids++,idt++;
else if(idt!=0) idt=bor[idt];
else ids++;
if(idt==lent){
same[++tot]=ids-1;
idt=bor[idt];
}
}
dp[0]=1;
int nowid=1;
for(int i=1;i<=lens;i++){
dp[i]=dp[i-1];
if(nowid<=tot&&i==same[nowid]) dp[i]=(dp[i]+dp[i-lent])%mod,nowid++;
}
cout<<dp[lens]%mod<<endl;
}
return 0;
}
fibonacci
一个序列,求其中最长斐波那契子序列长度。
其中“斐波那契子序列”指序列中每个数都等于前两个之和。
-
\(O(n^3)\) 的 \(dp\) 非常好想,考虑如何优化。
-
“用空间换时间”,开一个 \(map\) 记录原序列中数最早出现的位置。倒序遍历原序列,一边遍历一边记录当前最早出现的位置。
总之非常好理解,看代码就懂了。
#include<bits/stdc++.h>
using namespace std;
int dp[3003][3003],a[3003];
map<int,int> mp[3003];
int main(){
freopen("fibonacci.in","r",stdin);
freopen("fibonacci.out","w",stdout);
int n,ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++) dp[i][j]=2;
for(int i=n;i>=1;i--){
for(int j=i+1;j<=n;j++){
if(mp[j+1].count(a[i]+a[j])) dp[i][j]=max(dp[i][j],dp[j][mp[j+1][a[i]+a[j]]]+1),ans=max(ans,dp[i][j]);
}
mp[i]=mp[i+1],mp[i][a[i]]=i;
}
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号