DP

题目:ACM-ICPC 2018 焦作赛区网络预赛B---Mathematical Curse 

#include <bits/stdc++.h>
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Fov(i,x,y) for(int i=(x);i>=(y);--i)
#define Fo(i,x,y) for(int i=(x);i<(y);++i)
#define midf(a,b) ((a)+(b)>>1)
#define L(_) (_)<<1
#define R(_) ((_)<<1)|1
#define fi first
#define se second
#define ss(_) scanf("%s",_)
#define si(_) scanf("%d",&_)
#define sii(x,y) scanf("%d%d",&x,&y)
#define siii(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define sl(_) scanf("%lld",&_)
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
inline int read()
{
    char ch=getchar(); int x=0, f=1;
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
    while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
const ll inf=1e18+10;
const double pi=acos(-1.0);
const int n_max=1e3+10;
ll dp[n_max][10],tp[n_max][10],a[n_max];
char s[10];

int main()
{
	//freopen("in.txt","r",stdin);
	int T; si(T);
	while(T--)
    {
        int n,m,k; siii(n,m,k);
        For(i,1,n) sl(a[i]);
        ss(s+1);
        For(i,1,m)
        {
            dp[i-1][i]=-inf;
            tp[i-1][i]=inf;
        }
        For(i,0,n) dp[i][0]=tp[i][0]=k;
        For(j,1,m)
        {
            For(i,j,n)
            {
                dp[i][j]=dp[i-1][j];
                tp[i][j]=tp[i-1][j];
                ll x=dp[i-1][j-1],y=tp[i-1][j-1];
                if(s[j]=='+')
                {
                    x+=a[i];
                    y+=a[i];
                }
                else if(s[j]=='-')
                {
                    x-=a[i];
                    y-=a[i];
                }
                else if(s[j]=='*')
                {
                    x*=a[i];
                    y*=a[i];
                }
                else
                {
                    x/=a[i];
                    y/=a[i];
                }
                if(x<y) swap(x,y);
                dp[i][j]=max(dp[i][j],x);
                tp[i][j]=min(tp[i][j],y);
            }
        }
        printf("%lld\n",dp[n][m]);
    }
	return 0;
}

一、LCS(最长公共子序列)

题目:BZOJ1264---基因匹配Match

二、LIS(最长上升子序列)

 

#include <bits/stdc++.h>
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Fov(i,x,y) for(int i=(x);i>=(y);--i)
#define Fo(i,x,y) for(int i=(x);i<(y);++i)
#define midf(a,b) ((a)+(b)>>1)
#define L(_) (_)<<1
#define R(_) ((_)<<1)|1
#define fi first
#define se second
#define ss(_) scanf("%s",_)
#define si(_) scanf("%d",&_)
#define sii(x,y) scanf("%d%d",&x,&y)
#define siii(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
inline int read()
{
    char ch=getchar(); int x=0, f=1;
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
    while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
const double pi=acos(-1.0);
const int n_max=1e4+5;
ll dp[n_max];
int d[25];

int main()
{
	//freopen("in.txt","r",stdin);
	int T; si(T);
	d[0]=1;
	For(i,1,20) d[i]=d[i-1]<<1;
	while(T--)
    {
        int n,q,v,c,s; sii(n,q);
        mem(dp,0);
        dp[0]=1;
        For(i,1,n)
        {
            sii(v,c);
            Fo(j,0,c)
            {
                int x=d[j]*v;
                for(int k=10000;k-x>=0;--k) dp[k]=(dp[k-x]+dp[k])%mod;
            }
        }
        For(i,1,q)
        {
            si(s);
            printf("%lld\n",dp[s]);
        }
    }
	return 0;
}

四、树形DP

题目1: HDU1520---Anniversary party

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#define For(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
typedef long long ll;
const int n_max=6e3+5;
const int inf=0x3f3f3f3f;
int dp[n_max][2],head[n_max],fun[n_max],p[n_max],cnt;
struct node
{
    int to,next;
}e[n_max];
void add(int u,int v)
{
    e[++cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt;
}

void dfs(int cur)
{
    dp[cur][1]=fun[cur];
    for(int i=head[cur];i;i=e[i].next)
    {
        int v=e[i].to;
        dfs(v);
        dp[cur][0]+=max(dp[v][0],dp[v][1]);
        dp[cur][1]+=dp[v][0];
    }
}

int main()
{
    int n,u,v;
    scanf("%d",&n);
    For(i,1,n)
        scanf("%d",&fun[i]);
    while(~scanf("%d%d",&u,&v)&&u&&v)
    {
        p[u]=v;
        add(v,u);
    }
    int t=1;
    while(p[t]) t=p[t];
    dfs(t);
    printf("%d\n",max(dp[t][0],dp[t][1]));
	return 0;
}

题目2:POJ2378---Tree Cutting

以任意点为根dfs遍历整棵树,当去除某个节点时,会将树分成两棵子树,分别是向其父亲节点延伸和向其子节点延伸的树,对于节点i,定义dp[i]:去掉i产生的两棵树中节点数较大的数值,sum[i]:节点i的子树节点数(包括其本身);则有 dp[i]=max(n-sum[i],sum[k],k为他的子节点中子树节点数最大的一个);

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
#define midf(a,b) ((a)+(b)>>1)
#define Num1(_) (_)<<1
#define Num2(_) ((_)<<1)|1
using namespace std;
typedef long long ll;
inline int read()
{
    char ch=getchar(); int x=0, f=1;
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
    while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
const int inf=0x3f3f3f3f;
const int n_max=1e4+10;
int head[n_max],dp[n_max],sum[n_max],cnt,n;
struct edge
{
    int nex,to;
}e[2*n_max];
void add(int u,int v)
{
    e[++cnt].to=v;
    e[cnt].nex=head[u];
    head[u]=cnt;
}
int dfs(int cur,int last)
{
    sum[cur]=1;
    int temp=0;
    for(int i=head[cur];i;i=e[i].nex)
    {
        int v=e[i].to;
        if(v==last) continue;
        int x=dfs(v,cur);
        temp=max(temp,x);
        sum[cur]+=x;
    }
    dp[cur]=max(n-sum[cur],temp);
    return sum[cur];
}
int main()
{
	int u,v;
	n=read();
	For(i,1,n-1)
	{
	    u=read(),v=read();
	    add(u,v);
	    add(v,u);
	}
	dfs(1,0);
	int flag=0;
	For(i,1,n)
	{
	    if(2*dp[i]<=n)
        {
            flag=1;
            printf("%d\n",i);
        }
	}
	if(!flag)
        printf("NONE\n");
	return 0;
}

注:由于树的特性是无环,所以可以直接dfs(cur, last),如果由当前节点cur延伸出去的边中有边的端点为父亲节点last,则跳过此边,即可避免重复访问。

题目3:HDU6446---Tree and Permutation

先了解一下低配版:HDU2376---Average distance

设树有n个顶点,对于任意一条树边,如果将其去除,树将会分成两棵,点的数量分别为m和n-m,左右两侧的点都必须经过这条边到达另一边的点,因而对于某一条边,枚举所有路径,该边对路径和的贡献值为w*m*(n-m),dfs每条边即可得到路径和,总共有n!条路径;而dfs每条边可以转化为dfs每个点,这样低配版就可以解决;

对于高配版,对于任意全排列n!和某两个点i和j,将i和j看成整体与其他点进行全排列,有2*(n-1)!种,也就是以任意点i和j 为端点的路径要走2*(n-1)!次,再乘上上面得出该路径贡献值为2*(n-1)!*w*m*(n-m)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
#define midf(a,b) ((a)+(b)>>1)
#define Num1(_) (_)<<1
#define Num2(_) ((_)<<1)|1
using namespace std;
typedef long long ll;
inline int read()
{
    char ch=getchar(); int x=0, f=1;
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
    while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
const int n_max=1e5+10;
int head[n_max],cnt=0,n;
ll sum[n_max],fac[n_max],ans=0;
struct node
{
    int to,nex;
    ll w;
}e[n_max*2];
void add(int u,int v,ll w)
{
    e[++cnt].to=v;
    e[cnt].nex=head[u];
    e[cnt].w=w;
    head[u]=cnt;
}
void dfs(int u,int pre)
{
    sum[u]=1;
    for(int i=head[u];i;i=e[i].nex)
    {
        int v=e[i].to;
        ll w=e[i].w;
        if(v==pre) continue;
        dfs(v,u);
        sum[u]+=sum[v];
        ans=(ans+sum[v]*(n-sum[v])*w)%mod;
    }
}
int main()
{
	int u,v;
	ll w;
	fac[0]=fac[1]=1;
	for(ll i=2;i<=1e5;++i)
        fac[i]=fac[i-1]*i%mod;
	while(~scanf("%d",&n))
    {
        ans=0;
        cnt=0;
        memset(head,0,sizeof(head));
        For(i,1,n-1)
        {
            scanf("%d%d%I64d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        dfs(1,-1);
        printf("%I64d\n",2*ans*fac[n-1]%mod);
    }
	return 0;
}

五、概率DP

题目1:POJ2096--- Collecting Bugs

dp[i][j]: 目前已发现i种bug在j种子系统中,要达成目标的期望

dp[i][j]= (dp[i][j]+1)*p1+(dp[i+1][j+1]+1)*p2+(dp[i+1][j]+1)*p3+(dp[i][j+1]+1)*p4,

化简可得dp[i][j]=(1+dp[i+1][j+1]*p2+dp[i+1][j]*p3+dp[i][j+1]*p4)/(1-p1)

其中

可知dp[n][s]=0, dp[0][0]为所求结果

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#define For(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int n_max=1e3+5;
double dp[n_max][n_max];

int main()
{
	double n,s;
	scanf("%lf%lf",&n,&s);
	for(int i=n;i>=0;--i)
    {
        for(int j=s;j>=0;--j)
        {
            if(i==n&&s==j) continue;
            dp[i][j]=(n*s+dp[i+1][j+1]*(n-i)*(s-j)+dp[i+1][j]*(n-i)*j+dp[i][j+1]*i*(s-j))/(n*s-i*j);
        }
    }
    printf("%.4lf",dp[0][0]);
	return 0;
}

题目2:CodeForces 148D---Bag of mice

dp[i][j]:口袋里剩下i只白鼠和j只黑鼠时公主胜率

特殊情况:当i==0时,公主胜率为0;当j==0且i>=1时,公主胜率为1

一般情况:

如果公主抽到白鼠,对dp[i][j]贡献

如果公主抽到黑鼠,此时如果龙抽到白鼠公主获胜概率为0,对dp[i][j]无贡献;

接下来如果龙抽到黑鼠,然后会有两种情况,第一种是跑了白鼠,第二种是跑了黑鼠

因而最终为dp[i][j]贡献

注意判断j是否大于等于2或者大于等于3

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#include<utility>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
#define midf(a,b) ((a)+(b)>>1)
#define Num1(_) (_)<<1
#define Num2(_) ((_)<<1)|1
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
inline int read()
{
    char ch=getchar(); int x=0, f=1;
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
    while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int n_max=1e3+10;
double dp[n_max][n_max];

int main()
{
	int w,b;
	scanf("%d%d",&w,&b);
	For(i,0,w){
	    For(j,0,b){
	        if(i==0){
                dp[i][j]=0;
                continue;
	        }
	        if(j==0){
                dp[i][j]=1;
                continue;
	        }
	        dp[i][j]=1.0*i/(i+j);
	        if(j>=2)
                dp[i][j]+=dp[i-1][j-2]* 1.0*j/(i+j)* 1.0*(j-1)/(i+j-1)* 1.0*i/(i+j-2);
            if(j>=3)
                dp[i][j]+=dp[i][j-3]* 1.0*j/(i+j)* 1.0*(j-1)/(i+j-1)* 1.0*(j-2)/(i+j-2);
        }
	}
	printf("%.9lf\n",dp[w][b]);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值