dp小练

CCPC吉林D题,概率DP。暴力打表为2^n不可行,考虑每个点由什么状态转移过来。

设DP[i][j]表示到坚持到玩了i回合,赢了j次仍然没有结束的概率。

显然DP[i][j]=p1*DP[i-1][j]+p2*DP[i-1][j-1];//由上一场赢或者输转移过来。p1,p2表示分别的概率

然后赢的概率就从dp[i][j]下次必赢进行累加就是结果了。

#include 
using namespace std;
const int maxn=1e5+7;
const double eps=1e-6;
double dp[1000][800];
double count1(double x,double y){
    double res;
    res=(y+1)*0.02+(x-y)*0.015;
    if(res>=1.0)
    res=1;
    return res;
}
int main(){
    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
    //freopen("in.txt","r",stdin);
    //freopen("out1.txt","w",stdout);
    int n,m;
    double p,q;
    int T;
    cin >> T;
    while(T--){
    memset(dp,0,sizeof(dp));
    cin >> p;
    q=0.02;
    p/=100.0;
    dp[1][1]=p*(1-q);
    dp[1][0]=(1-p);
    for(i=2;i<=500;i++){
        for(j=0;j<=i;j++){
            dp[i][j]+=p*(1-count1(i-1,j-1))*dp[i-1][j-1]+(1-p)*dp[i-1][j];
        }
    }
    double res=0;
    for(i=1;i<=500;i++)
        for(j=0;j<=i;j++){
            res+=(i+1.0)*p*(count1(i,j)*dp[i][j]);
        }
    printf("%.10lf\n",res+0.02*p);
    }
    return 0;
}

落谷P1020:

求最长不上升序列的长度nlogn,然后有多少个就是最长上升序列,这个结论。。记住就好

落谷P1091,合唱队形:

给一个序列,问使得序列a[i]a[i+2]的形式,最少需要删除多少个人,dp[i][j]表示到第i个人,末尾身高为j删除了多少个人。这里wa了好多,min(t1,min())的写法需要注意

落谷P1282:相似基因

给两串为ACTG的基因,每个基因可以和另一串的下一个基因匹配或者和空的匹配,每种匹配方案有不同的相似度,求最大的相似度。dp注意边界问题。

dp[i][j]表示第i个基因匹配到位置j的最大相似度值。那么每次考虑转移要么2个都匹配,要么期中一个与空匹配即可得到结果

落谷P1220:关路灯

方法一:dp[i][j]表示目前在位置i,状态j的最小代价,这个j因为是long long的大小所以用了个map来储存,每次往左边或者往右边,进到一个新的位置就判断这个状态这个点的花费是不是最小,搜索的方法。

方法二:dp[i][j][k]期中k表示两个状态在i点和在j点,dp[i][j]表示的为i到j的路灯全部熄灭了。

那么dp[i][j][0]可以为最后一个关灯的是i/j,

如果是i,那么其由dp[i+1[j][0]与dp[i+1][j][1]转移

如果是j,那么其由dp[i][j-1][0]与dp[i][j-1][1]转移,也就是一个状态由4个状态转移而来

这里因为左端点不会大于m,右端点不会小于m,所以

for(i=m;i>=1;i--)
        for(j=m;j<=n;j++)

的转移方式。

#include
using namespace std;
typedef long long ll;
const ll maxn=1e5+7;
const ll inf=1e17;
ll q1[maxn],q2[maxn],q3[maxn];
ll qq[maxn];
struct ttt{
    ll num,status,lei;
};
ll pos[maxn];
ll dp[100][100][2];
ll sum1[maxn];
int main(){
    ll i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,t5,n,m;
    //freopen("in.txt","r",stdin);
    scanf("%lld %lld",&n,&m);
    t2=0;
    for(i=1;i<=n;i++){
        scanf("%lld %lld",&pos[i],&q1[i]);
        t2+=q1[i];
        sum1[i]=sum1[i-1]+q1[i];
    }
    ll S=sum1[n];
    if(n==1){
    printf("0\n");return 0;
    }
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            dp[i][j][0]=dp[i][j][1]=inf;
    dp[m][m][0]=dp[m][m][1]=0;
    for(i=m;i>=1;i--){
        for(j=m;j<=n;j++){
            if(i==m&&j==m)continue;
            dp[i][j][0]=min(min(dp[i+1][j][0]+(pos[i+1]-pos[i])*(S-(sum1[j]-sum1[i])),
                            dp[i+1][j][1]+(pos[j]-pos[i])*(S-(sum1[j]-sum1[i]))),
            min(dp[i][j-1][0]+(pos[j]-pos[i])*(S-(sum1[j-1]-sum1[i-1]))+(pos[j]-pos[i])*(S-(sum1[j]-sum1[i-1])),
                dp[i][j-1][1]+(pos[j]-pos[j-1])*(S-(sum1[j-1]-sum1[i-1]))+(pos[j]-pos[i])*(S-(sum1[j]-sum1[i-1]))));
            dp[i][j][1]=min(min(dp[i+1][j][0]+(pos[i+1]-pos[i])*(S-(sum1[j]-sum1[i]))+(pos[j]-pos[i])*(S-(sum1[j]-sum1[i-1])),
                            dp[i+1][j][1]+(pos[j]-pos[i])*(S-(sum1[j]-sum1[i]))+(pos[j]-pos[i])*(S-(sum1[j]-sum1[i-1]))),
            min(dp[i][j-1][0]+(pos[j]-pos[i])*(S-(sum1[j-1]-sum1[i-1])),
                dp[i][j-1][1]+(pos[j]-pos[j-1])*(S-(sum1[j-1]-sum1[i-1]))));
        }
    }
    cout << min(dp[1][n][0],dp[1][n][1]) <

落谷P1169:

每个叶子有一个权值为正得分,每条路径有一个负权值,问选择哪些叶子权值和>=0使得叶子数量最大,

要一个叶子需要把其到根节点1的所有路径权值全部拿上。

dp[i][j]表示i节点有j个串的最小费用,其需要枚举的k表示的为叶子数,那么每个节点叶子数为n,复杂度n2

那么转移就为树形背包

for(int j=sz[x];j>=1;j--){
                for(int k=1;k<=sz[v];k++)
                dp[x][j]=min(dp[x][j],dp[v][k]+dp[x][j-k]);
            }
#include
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
int dp[3002][3002],tot,head[maxn],q1[maxn];
const int inf=1e9+7;
struct E{
    int next,to,w;
}e[maxn+maxn];  //链式前向星要开双倍的大小
void add(int u,int v,int w){ //如果重复使用n+1,n+2个点建立边,记得把head[n+1]=-1
    tot++;
    e[tot].to=v;
    e[tot].w=w;
    e[tot].next=head[u];
    head[u]=tot;
}
int n,sz[maxn];
int dfs(int x){
    if(head[x]==-1){
    dp[x][1]=-q1[x];
    sz[x]=1;
    }else{
        int v;
        for(int i=head[x];i!=-1;i=e[i].next){
            v=e[i].to;
            dfs(v);
            sz[x]+=sz[v];
            for(int k=1;k<=sz[v];k++)
                 dp[v][k]+=e[i].w;

            for(int j=sz[x];j>=1;j--){
                for(int k=1;k<=sz[v];k++)
                dp[x][j]=min(dp[x][j],dp[v][k]+dp[x][j-k]);
            }
        }
    }
}
int main(){
    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
    int m;
    //freopen("in.txt","r",stdin);
    scanf("%d %d",&n,&m);
    memset(head,-1,sizeof(head));
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
        dp[i][j]=inf;
    for(i=1;i<=n-m;i++){
        scanf("%d",&t1);
        for(j=1;j<=t1;j++){
            scanf("%d %d",&f1,&f2);
            add(i,f1,f2);
        }
    }
    for(i=n-m+1;i<=n;i++){
        scanf("%d",&q1[i]);
    }
    dfs(1);
    t1=0;
    for(i=1;i<=n;i++)
    if(dp[1][i]<=0)t1=i;
    cout <

落谷P1169:

给一个01矩阵,求最大正方形和矩形,这里要求正方形与矩形为01间隔。

显然对于一个位置来说,如果其为一个正方形的右下角,那么其为上方与左方的不同数字的最大正方形与左上方相同数字的最大正方形之间取个min+1,就是这个位置的最大正方形。

这里得到每个位置最大正方形的大小,对于每一行,可以考虑矩阵的高度如果为4,那么必然是连续的>=4的数字组成的结果,

那么就相当于找一个(长度+高度-1)*高度为矩形的面积,用单调栈可以维护,左右+上下各做一遍单调栈即为答案.

#include
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
const int inf=1e9+7;
int dp[2002][2002][2];
int q1[2002][2002];
int q2[2002][2002];
struct ttt{
    int x,y;
};
int map1[2002][2002];
int map2[2002][2002];
ttt S[maxn];
int main(){
    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,m,n;
    //freopen("in.txt","r",stdin);
    scanf("%d %d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++){
            scanf("%d",&q1[i][j]);
            q2[j][i]=q1[i][j];
        }
    t2=0;
    for(i=1;i<=n;i++)
    for(j=1;j<=m;j++){
        if(q1[i][j]==1){
        t1=min(dp[i-1][j][0],dp[i][j-1][0]);
        t1=min(t1,dp[i-1][j-1][1]);
        t1++;
        dp[i][j][1]=t1;
        }else{
        t1=min(dp[i-1][j][1],dp[i][j-1][1]);
        t1=min(t1,dp[i-1][j-1][0]);
        t1++;
        dp[i][j][0]=t1;
        }
        t1=t1*t1;
        t2=max(t2,t1);
    }
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            map1[i][j]=dp[i][j][q1[i][j]];
            map2[j][i]=map1[i][j];
        }
    }
    int max1=t2;
    int top;
    ttt u;
    for(i=1;i<=n;i++){
        top=0;S[++top].x=1;S[top].y=1;
        for(j=2;j<=m;j++){
            if(q1[i][j]==q1[i][j-1]){ //必然不相邻
            while(top>=1){
                max1=max(max1,(j-S[top].x+S[top].y-1)*S[top].y);
                top--;
            }top=1;
            S[1].x=j;S[1].y=1;continue;
            }
            u.x=j;u.y=map1[i][j];
            while(top>=1&&u.y<=S[top].y){
                    max1=max(max1,(j-S[top].x+S[top].y-1)*S[top].y);
                    u.x=S[top].x;
                    top--;
            }
            S[++top]=u;
        }
        while(top>=1){
                max1=max(max1,(m+1-S[top].x+S[top].y-1)*S[top].y);
                top--;
        }
    }
    for(i=1;i<=m;i++){
        top=0;S[++top].x=1;S[top].y=1;
        for(j=2;j<=n;j++){
            if(q2[i][j]==q2[i][j-1]){
            while(top>=1){
                max1=max(max1,(j-S[top].x+S[top].y-1)*S[top].y);
                top--;
            }top=1;
            S[1].x=j;S[1].y=1;continue;
            }
            u.x=j;u.y=map2[i][j];
            while(top>=1&&u.y<=S[top].y){
                    max1=max(max1,(j-S[top].x+S[top].y-1)*S[top].y);
                    u.x=S[top].x;
                    top--;
            }
            S[++top]=u;
        }
        while(top>=1){
                max1=max(max1,(n+1-S[top].x+S[top].y-1)*S[top].y);
                top--;
        }
    }
    cout <

 

你可能感兴趣的:(算法)