周六日常训练,背包dp,树形dp,简单dp以及很多数学?


队内周六日常训练,我迟到了半个小时,然后两个dalao开始疯狂过题,这比赛是很多场现场赛组合来的,大概都是铜牌、铁牌,少部分银牌题。 比较适合我们队伍的情况。

A
似乎是二分图匹配? 简单的? 熊神交了几发就过了。
B
题意很简单:给a , b 求x,y 满足 x+y = a 且 lcm(x,y) = b
高中数学题
哼! 不是常说
模拟只会猜题意,贪心只能过样例。图论只会套模板,数论只会gcd
这下好了,出gcd了,你看还不是不会!

从x,y下手。我们设 gcd(x,y) = c
所以 x = i*c , y = j*c (i,j互质)
于是有:
lcm(x,y) = c*i*j = b
ic + jc = a = c*(i+j)

因为 i j 互质 i*j 的因数只有i和j 和1 和自己
所以 gcd(i+j , i*j) = gcd(i,i*j) 或者gcd(j,i*j);
假设 gcd(i,i*j)=x(x!=1)
则有:
i=ax
i*j=bx => j=(a-b)x 且可知a>b 所以 j=cx ,所以gcd(i,j)!=1 ,与题设矛盾
所以 gcd(i+j , i*j) =1 所以 gcd ( a,b ) = c

所以 i + j = a/c 且 i*j = b/c
然后就是解二元一次方程组。。。所以只需解出最小的i即可

C
给一个x 可以分成任意段:x1+x2+x3….,必须满足:
1.任何两段都不相同
2.求x1*x2*x3最大值
算了,不强行分析了,数论不是gcd,超出我的能力范围了。

D
点分治,熊神补了,先不看了

E
盒子里面有k个黑球,1个红球,谁先取出红球谁就胜利,问先手会有优势吗?
一开始没想好,以为后手会有优势,sb了一下。
k=1 一黑一红 先手既无优势也无劣势,55k
k=2 两黑一红,先手取1/3赢 即使没取到 轮到后手时 也是55k ,所以先手有优势。
k=3 三黑一红
000X
先手: 1/4 赢 3/4取不到
后手: 必须建立在先手取不到的情况: P(赢)=3/4 * 1/3 =1/4
然后55k。 所以 先手既无优势也无劣势,55k
k=4 0000X 先手取完一个之后,情况和k=3相同,所以先手肯定有优势
往后推 同理

F
貌似是个挺简单的计算几何,拆成三角形计算就可以了。 s=a*b*sinx*1/2?

G
化成二进制,然后再换成八进制,4位->1位

H
题意:一条路上有n条河,每条河有起点和终点,河里面会 随机在任何位置出现一条船,并且船的速度恒等于V,但是方向向左向右是随机的,走路的速度=1,求从A 走到B 的期望时间。
对单独一条河,过河的期望= 当船在河中心 的t(v向左)+t(v向右),这个怎么证明呢,我只能说根据题目条件可得= =,然后我们把所有河长度加起来当成一条河就可以了。

I
下载东西,一大堆规则,其实下载的总量是固定的,总速度是固定的,除一下就是答案。

J
HDU 3236
有很多物品,每个物品有体积,价值,属性(有些必须买,有些不必须)
给两个背包(v1,v2),跑0-1背包, 以及可以免费选任何一个物品。
求最大价值。
首先我们把物品分为必须和非必须
然后跑两遍0-1 背包,根本不需要记录我们选择了哪些物品
但必须记录我们选择完必须品之后的状态 dp[i][j] 。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<string.h>
#include<map>
#include<set>
using namespace std;
template<class T> T gcd(T a,T b){return b?gcd(b,a%b):a;}

int dp[505][55][2];
//dp[i][j]代表使用的容量是i和j的最大价值, 再开一维表示免费的机会即可
struct node{
    int v,w;
    bool operator <(const node &b)const{
        return v>b.v;
    }
};
priority_queue<node>q;
int main(){
    int V1,V2,i,j,a,b,c,k,n,ans,cas,sum,pre,sign;
    cas=1;
    while(~scanf("%d%d%d",&V1,&V2,&n)&&(V1||V2||n)){
        vector<node> G[2];
        memset(dp,-1,sizeof(dp));
        for(i=0;i<n;i++){
            scanf("%d%d%d",&a,&b,&c);   // 分别是 体积和价值,是否必须
            G[c].push_back((node){a,b});
        }
        sum=dp[0][0][0]=0;
        for(i=0;i<G[1].size();i++){
            pre=sum;
            sum+=G[1][i].w;
            for(j=V1;j>=0;j--){  // 第一个背包和第二个背包都必须扫
                for(k=V2;k>=0;k--){
                    if(dp[j][k][0]==pre) //如果没用免费机会时,最大价值=pre
                        dp[j][k][1]=sum;   //则用了免费会变成 这样
                    if(j>=G[1][i].v){
                        if(dp[j-G[1][i].v][k][1]==pre)
                            dp[j][k][1]=sum;
                        if(dp[j-G[1][i].v][k][0]==pre)
                            dp[j][k][0]=sum;
                    }
                    if(k>=G[1][i].v){
                        if(dp[j][k-G[1][i].v][1]==pre)
                            dp[j][k][1]=sum;
                        if(dp[j][k-G[1][i].v][0]==pre)
                            dp[j][k][0]=sum;
                    }
                }
            }
        }
        sign=0;
        for(i=0;i<=V1;i++)
            for(j=0;j<=V2;j++)
                for(k=0;k<=1;k++){
                    if(dp[i][j][k]!=sum)  //检测是否能把必需品装下,并且我们知道剩下的状态是怎样
                        dp[i][j][k]=-1;
                    else
                        sign=1;  //我们保存了使用i,j空间装满所有必需品的状态
                }
        if(!sign){
            printf("Case %d: -1\n\n",cas++);
            continue;
        }                                       //
        for(i=0;i<G[0].size();i++){
            for(j=V1;j>=0;j--){
                for(k=V2;k>=0;k--){
                    if(dp[j][k][0]!=-1)         //要放在最前面,避免了重复装入
                        dp[j][k][1]=max(dp[j][k][1],dp[j][k][0]+G[0][i].w);
                    // 必须满足!=-1 即:把必需品装完
                    if(j>=G[0][i].v){
                        if(dp[j-G[0][i].v][k][1]!=-1)
                            dp[j][k][1]=max(dp[j][k][1],dp[j-G[0][i].v][k][1]+G[0][i].w);
                        if(dp[j-G[0][i].v][k][0]!=-1)
                            dp[j][k][0]=max(dp[j][k][0],dp[j-G[0][i].v][k][0]+G[0][i].w);
                    }
                    if(k>=G[0][i].v){
                        if(dp[j][k-G[0][i].v][1]!=-1)
                            dp[j][k][1]=max(dp[j][k][1],dp[j][k-G[0][i].v][1]+G[0][i].w);
                        if(dp[j][k-G[0][i].v][0]!=-1)
                            dp[j][k][0]=max(dp[j][k][0],dp[j][k-G[0][i].v][0]+G[0][i].w);
                    }
                }
            }
        }
        ans=0;
        for(i=0;i<=V1;i++)
            for(j=0;j<=V2;j++)
                for(k=0;k<=1;k++)
                    ans=max(ans,dp[i][j][k]);
        printf("Case %d: %d\n\n",cas++,ans);
    }
    return 0;
}

K
树形dp, 我觉得自己写的没错,但是hdu上就是只写输入都会Tle,根本不知道怎么回事,只能贴一下自己代码了
我的做法:
dfs 回溯,在记录深度,根据深度来选择符合条件的最大或最小值。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<string.h>
#include<map>
#include<set>
using namespace std;
#define ll __int64
#define N 500002
struct Edge{
    int to,di;
    int next;
};
Edge edge[N*2];
int head[N],tot;
void addedge(int u,int v,int d){
    edge[tot].to=v;
    edge[tot].di=d;
    edge[tot].next=head[u];
    head[u]=tot++;
}

int dis[N],L,R;
int deep[N],n;
int ans[N];
void dfs(int u,int pre){
    int flag=0;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int to=edge[i].to;
        int l=edge[i].di;
        if(to==pre) continue;
        flag=1;
        deep[to]=deep[u]+1;
        if(dis[u]>R)
            dis[to]=R+1;
        else
            dis[to]=dis[u]+l;
        dfs(to,u);
    }
    if(!flag) //叶子
        if(L<=dis[u] && dis[u]<=R)
            ans[u]=dis[u];

    if(ans[u]!=-1){
        if(ans[pre]==-1 )
            ans[pre]=ans[u];
        else{
            if(deep[pre]&1)
                ans[pre]=max(ans[pre],ans[u]);
            else
                ans[pre]=min(ans[pre],ans[u]);
        }
    }

}
void init(int n){
    memset(dis,0,sizeof(dis));
    memset(ans,-1,sizeof(ans));
    memset(head,-1,sizeof(head));
    deep[0]=1;
    dis[0]=tot=0;   //所以deep奇数选最大,偶数选最小
}
int main()
{
    //freopen("1.txt","r",stdin);
    while(~scanf("%d %d %d",&n,&L,&R)){
        init(n);
        int a,b,c;
        for(int i=1;i<n;i++){
            scanf("%d %d %d",&a,&b,&c);
            addedge(a,b,c);
            addedge(b,a,c);
        }
        dfs(0,0);
        if(ans[0]==-1) printf("Oh, my god!\n");
        else
            printf("%d\n",ans[0]);
    }

    return 0;
}

M
简单dp 都不会也是醉了
只需要想到,对于全排列从 x 到 x+1 长度增加1 对E的影响 要么就不变,要么就+1
于是很简单就可以写出dp方程。。。

N简单的最短路,十个点,任何算法都能过。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值