您的位置:首页 > 其它

[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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: