[WC 2015复习](五)动态规划
2015-01-28 18:23
197 查看
都是比较简单SB的东西,求各位去WC的神犇勿喷。
1、利用数据结构优化动态规划
(1)[BZOJ 1911][Apio 2010]特别行动队 (利用单调队列对DP进行斜率优化)
http://www.lydsy.com/JudgeOnline/problem.php?id=1911#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 1000100 using namespace std; typedef long long int LL; LL n,f[MAXN],sum[MAXN]; //sum[] LL q[MAXN],h,t,a,b,c; double slope(int x,int y) // { return (double)((f[x]+a*sum[x]*sum[x]-b*sum[x])-(f[y]+a*sum[y]*sum[y]-b*sum[y]))/(2*a*(sum[x]-sum[y])); } int main() { scanf("%lld%lld%lld%lld",&n,&a,&b,&c); for(int i=1;i<=n;i++) { LL x; scanf("%lld",&x); sum[i]=sum[i-1]+x; } f[0]=0; h=t=1; q[1]=0; for(int i=1;i<=n;i++) { while(h<t&&slope(q[h+1],q[h])<=sum[i]) h++; f[i]=f[q[h]]+a*(sum[i]-sum[q[h]])*(sum[i]-sum[q[h]])+b*(sum[i]-sum[q[h]])+c; while(h<t&&slope(q[t],q[t-1])>slope(i,q[t])) t--; q[++t]=i; } printf("%lld\n",f ); return 0; }
2、数位DP
(1)[BZOJ 1026][SCOI 2009]windy数
http://www.lydsy.com/JudgeOnline/problem.php?id=1026首先DP预处理f[][]数组,f[i][j]表示长度为i的数字,最高位为j(可以为0)的windy数个数。
比较显然的思路就是求[a,b]之间的windy数个数,相当于求[1,b+1)中的windy数个数-[1,b+1)中的windy数个数。问题转化为求[1,x)中的windy数个数。我们可以通过取模得到x的每一位的数字以及x的长度cnt,例如x=13579的情况时,我们相当于求
1~9999中的windy数个数(长度小于cnt的windy数个数)+10xxx~13xxx形式的windy数个数+130xx~134xx形式的windy数个数+1350x~1356x的windy数个数+1357x形式的windy数个数。
注意若碰到不合法情况,例如求10xxx~12xxx形式的windy数个数,显然|1-0|<2,|1-2|<2,10xxx~12xxx的数字都不是windy数,就不用继续统计后面的数字了
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 15 #define MAXM 10 using namespace std; typedef long long int LL; int f[MAXN][MAXM]; //f[i][j]=最高位为i位,最高位数字为j的windy数个数 int digit[MAXN],cnt=0; //x有cnt位长,每一位的数字保存在digit数组中,数组中下标1的是个位 void init(int n) //预处理出数字x的每一位,保存在digit数组中 { if(n==0) return; digit[++cnt]=n%10; init(n/10); } void DP() //DP预处理 { for(int i=0;i<=9;i++) f[1][i]=1; //初始化 for(int i=2;i<MAXN;i++) for(int j=0;j<=9;j++) for(int k=0;k<=9;k++) if(abs(j-k)>=2) f[i][j]+=f[i-1][k]; } LL cal(int x) //求[1,x]中的windy数个数 { LL ans=0; cnt=0; memset(digit,0,sizeof(digit)); init(x); //先求出位数不到cnt的windy数个数 for(int i=1;i<cnt;i++) for(int j=1;j<=9;j++) //!!!!!!!!!!! ans+=f[i][j]; for(int i=1;i<digit[cnt];i++) //再加上位数为cnt,最高位比x最高位小的windy数个数 ans+=f[cnt][i]; for(int i=cnt-1;i>0;i--) //枚举剩下的第i位 { for(int j=0;j<digit[i];j++) //枚举第i位为数字j,j要比x的第i位小 if(abs(digit[i+1]-j)>=2) ans+=f[i][j]; if(abs(digit[i+1]-digit[i])<2) break; //x的第i位和第i+1位之差小于2,那么后面的不需要再枚举了 } return ans; } int main() { int a,b; cin>>a>>b; DP(); cout<<cal(b+1)-cal(a)<<endl; return 0; }
3、基环树DP
(1)[BZOJ 1040][ZJOI 2008]骑士
http://www.lydsy.com/JudgeOnline/problem.php?id=1040此题就是没有上司的舞会的基环树林版本。我们可以先用一次DFS求出树林中每棵基环树上可以形成环的那条边E=U->V,或者说是去掉这条边就可以把基环树变成一般的树,然后我们删掉这条边,由于这条边删掉后,U和V中只能取一个或者都不取,因此要分两次进行树上DP,一次以U为起点树上DP,一次以V为起点树上DP。把两次f[U][0]和f[V][0]中取较大者计入答案即可。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1000100
using namespace std;
typedef long long int LL;
struct edge
{
int u,v,next;
}edges[MAXN*2];
int head[MAXN],nCount=1;
void AddEdge(int U,int V)
{
edges[++nCount].u=U;
edges[nCount].v=V;
edges[nCount].next=head[U];
head[U]=nCount;
}
bool visit[MAXN];
int val[MAXN];
LL f[MAXN][2],ans=0; //f[i][0]=不取点i得到的最大分数,f[i][0]=取点i得到的最大分数
int U,V,E; //去掉边E=(U->V)后,基环树变成了一般的树
void DFS(int u,int last) //找形成基环树的那条边E,last=链接u和父亲的边
{
visit[u]=true;
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if((p^1)==last) continue;
if(visit[v]) //找到了形成基环树的那条边
{
U=u;
V=v;
E=p;
continue;
}
DFS(v,p);
}
}
void DP(int u,int last,int rolle) //last是连接u父亲和u的边,rolles是构成基环树的边
{
f[u][1]=val[u]; //初始化
f[u][0]=0;
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if(p==rolle||(p^1)==rolle) //边p是之前标记的构成基环树的边,不能走
continue;
if((p^1)==last) continue;
DP(v,p,rolle);
f[u][1]+=f[v][0];
f[u][0]+=max(f[v][0],f[v][1]);
}
}
int main()
{
memset(head,-1,sizeof(head));
nCount=1;
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int v;
scanf("%d%d",&val[i],&v);
AddEdge(i,v);
AddEdge(v,i);
}
for(int i=1;i<=n;i++) //枚举将每个点作为DFS的起点(每个基环树的根)
{
if(!visit[i])
{
DFS(i,0);
DP(U,0,E);
LL tmp=f[U][0];
DP(V,0,E);
tmp=max(tmp,f[V][0]);
ans+=tmp;
}
}
printf("%lld\n",ans);
return 0;
}
相关文章推荐
- [WC 2015复习](三)图论算法与经典模型
- 动态规划复习-HDU1159
- 【NOIP2015提高组T5】子串-字符串上的动态规划
- NYOJ 18 The Triangle (动态规划复习)
- [NOIP复习]第三章:动态规划
- NYOJ171 聪明的kk (动态规划复习)
- 动态规划:划分数 复习
- [NOIP 2014复习]第三章:动态规划——NOIP历届真题回想
- NOIP专题复习——专题二:动态规划基础
- 动态规划复习-HDU2084
- 动态规划:最长公共子序列 复习
- [WC 2015复习](一)中级数据结构与分治算法
- 动态规划复习-HDU1087
- 【算法复习二】货郎担(旅行售货商)动态规划
- [NOIP复习]第三章:动态规划
- 动态规划:01背包 复习
- 【算法复习二】货郎担(旅行售货商)动态规划
- 2015百度校招之动态规划(兼职问题)
- 着手SDUT OJ提高实验—动态规划,之前对动态规划的复习#Round1
- [WC 2015复习](二)与字符串有关的算法及数据结构