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

来源:互联网 发布:c语言幻数什么意思 编辑:程序博客网 时间:2024/05/21 10:08


队内周六日常训练,我迟到了半个小时,然后两个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 500002struct 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
原创粉丝点击