关于联通块DP的一些问题(to be continued)

来源:互联网 发布:java microservice 编辑:程序博客网 时间:2024/05/29 10:26

记一下这种奇妙的思路。。。


Example I:HDU 6157

题意就是给一些点(数轴上),然后你要选出其中的m个点,重新安排它们的顺序,假设为P1...Pm.

若令P0=Pm,Pm+1=P1(其实就是在一个环上)

那么这个排列的权值就是dist(P1,P2)+...+dist(Pm,P1)+(满足Pi-1<Pi>Pi+1或Pi-1>Pi<Pi+1的点个数)*d[0]


思路:dp[i][j][k]表示到第i个点,有j个不相交路径,已经选了k个checkpoint了.

可以发现,如果顺序已经被钦定,那么从这个排列的任意一个点开始答案都一样.

令s[i]=[j=1...i-1]d[j]

所以dp的同时可以统计答案,就拿当前点为起点.然后发现点贡献一定是2*s[i]+d[0]或-2*s[i]+d[0]


转移如下:dp[i][j][k]

不选这个点->dp[i+1][j][k]

选了这个点但不在转折点上->dp[i+1][j][k+1]

将两个路径通过i连接起来(+2*s[i]+d[0])->dp[i][j-1][k+1]

将i作为新的路径(-2*s[i]+d[0])->dp[i][j+1][k+1]

计算答案->ans=max(ans,dp[i][j][k]+2*s[i]+d[0]);


噫...

#include<bits/stdc++.h>#define inf 1000000007using namespace std;int dp[2][110][110],ans,np=0,p=1,n,m,a[110],s[110];void upd(int& x,int y){x=max(x,y);}int main(){while(scanf("%d%d",&n,&m)==2){for(int i=0;i<=n-1;++i)scanf("%d",&a[i]);for(int i=1;i<=n-1;++i)s[i]=s[i-1]+a[i];for(int i=0;i<=n;++i)for(int j=0;j<=m;++j)dp[p][i][j]=dp[np][i][j]=-inf;ans=-inf;dp[np][1][1]=a[0],dp[np][0][0]=0;for(int i=2;i<=n;++i,swap(p,np))for(int j=0;j<=n;++j)for(int k=0;k<=m;++k)if(dp[np][j][k]!=-inf){int x=dp[np][j][k];//printf("[%d,%d,%d=%d]\n",i,j,k,x);if(k==m-1&&j==1)ans=max(ans,x+2*s[i-1]+a[0]);if(j>=2)upd(dp[p][j-1][k+1],x+a[0]+2*s[i-1]);upd(dp[p][j+1][k+1],x+a[0]-2*s[i-1]);upd(dp[p][j][k],x);if(j)upd(dp[p][j][k+1],x);dp[np][j][k]=-inf;}printf("%d\n",ans);}}


Example II:Codefoces某道题

现在有个迷之k级树:就是一个深度为k的二叉树所有节点向他的所有祖先连边,然后计算有向简单路径的数量.(即经过的点,边不重复)


思路:考虑dp[i][j]表示一个迷之i级树有j个不相交路径的方案数.

转移就考虑将两颗i-1级树合并,枚举dp[i-1][j],dp[i-1][k]

根作为一个新的路径->dp[i+1][j+k+1]

根连接两个路径->dp[i+1][j+k-1]

根不选或根连接一个路径->dp[i+1][j+k]

这样转移是O(n^3)...(其实还可以O(n^2logn),发现这个形式类似卷积然后就FFT...)

原创粉丝点击