2.7数据结构与算法学习日记(动态规划01背包和并查集)

题目描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

输入格式

第一行有 2 个整数 T(1≤T≤1000)和 M(1≤M≤100),用一个空格隔开,T 代表总共能够用来采药的时间,M 代表山洞里的草药的数目。

接下来的 M 行每行包括两个在 11 到 100100 之间(包括 11 和 100100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出在规定的时间内可以采到的草药的最大总价值。

输入输出样例

输入 #1复制

70 3
71 100
69 1
1 2

输出 #1复制

3

说明/提示

【数据范围】

  • 对于 30%30% 的数据,M≤10;
  • 对于全部的数据,M≤100。

【题目来源】

NOIP 2005 普及组第三题

题目分析

1,这可以理解为是01背包的模板题,接下来讲一下动态规划里的01背包问题(在这里我用的是二维来解决01背包)

2,提到01背包我们就得考虑俩种情况(对于一个物品取和不取)先设计一个dp【】【】二维数组

dp[i][j]代表的是以j为容量所放入的前i个物品的最大价值,然后开始分析俩种情况,如果是不取这个物品的话(你不要这个物品的话(或当前容量小于物品所需容量),那此时你的dp[i][j]里存放的价值是不是你不加他之前的最大价值,那么你此时的最大价值没变所以你的dp[i][j]会等于dp[i-1][j])因为你不取它,那么容量不用动,只需取你前i-1个物品的最大价值就行,所以

dp[i][j]=dp[i-1][j];

3,接下来考虑取它的情况,既然要取它的话那么(相当于把它放冰箱里)得先在冰箱里腾出它的位置,然后将他放入,放入之后,冰箱此时总质量是不是就会加上它(这个物品)的质量所以

dp[i][j]=dp[i-1][j-w[i]]+v[i]

(w[]代表质量,此时冰箱为了给它腾出空间,所以减去了它的质量好让它能放进来,然后i-1因为是在前i-1个物品的基础上对dp[i][j]动手脚)最后再加上它的质量也就是把它放进去)

4,最后对于取和不取,我们要有个判断,如果当前容量小于物品容量,那肯定不取,因为根本就装不下,那反之呢,反之我们就比较取和不取哪个更划算,所以我们要对这俩种方法进行比大小,这样才知道取和不取哪种更好

5,然后就是遍历顺序,我们选择先遍历物品,再遍历当前背包容量(在做dp题的时候记得多考虑dp数组所代表的含义)每次前i个物品去找取得的最大价值,所以答案就会是数组的最后一个元素

6,回到这个题目就把时间变成物品容量,草药就是物品,T就是背包容量(每次循环不是当前的,它只是最大的)

代码示例

#include
using namespace std;
int w[105],v[105];
int dp[105][1005];
int main()
{
    int t,m;
    scanf("%d%d",&t,&m);
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d",&w[i],&v[i]);
    }
    for(int i=1; i<=m; i++)//遍历物品
        for(int j=0; j<=t; j++)//遍历背包容量
        {
            if(j

题目描述

如题,现在有一个并查集,你需要完成合并和查询操作。

输入格式

第一行包含两个整数 N,M ,表示共有 N 个元素和 M 个操作。

接下来 M 行,每行包含三个整数 Zi​,Xi​,Yi​ 。

当 Zi​=1 时,将 Xi​ 与 Yi​ 所在的集合合并。

当 Zi​=2 时,输出 Xi​ 与 Yi​ 是否在同一集合内,是的输出 Y ;否则输出 N 。

输出格式

对于每一个 Zi​=2 的操作,都有一行输出,每行包含一个大写字母,为 Y 或者 N 。

输入输出样例

输入 #1复制

4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4

输出 #1复制

N
Y
N
Y

说明/提示

对于 30% 的数据,N≤10,M≤20。

对于 70% 的数据,N≤100,M≤103。

对于 100%100% 的数据,1≤N≤104,1≤M≤2×105,1≤Xi​,Yi​≤N,Zi​∈{1,2}。

题目分析

1,首先题目是一个并查集模板题,所以我们得了解并查集,下面来介绍下并查集(并查集先开始初始赋值时每个人的老大都是自己)

并查集(Disjoint Set)是一种数据结构,用于管理一组不相交的集合。它支持两种操作:查找(Find)和合并(Union)。并查集通常用于解决元素分组和连接问题,如连通性问题、最小生成树算法中的边的连接等。

2,对于这个题掌握并查集的查找和合并就ok,接下来就来解决这俩个函数,首先说查找,查找在这里里面相当于找他是哪个组织的(判断他是哪个组织的只需要看他的老大是谁)所以这是一个找老大的过程(你有重要情报告诉老大但你得先跟老六禀告因为他是你的上级,老六得禀告给老五,老五又得向老四禀告,依次下去,老大反而成为最后知道的了)所以我们得换个法子,不然老六得到情报策反了怎么办?所以我们换个法子让所有人的上级都是老大,意思是所有人有什么情报就直接向老大禀告,不用跟自己各自的上级说,直接找老大说,这种方法在这里叫做路径压缩,相当于把老大的地址告诉给了所有人,出事直接找老大

int find(int k){
    //路径压缩
    if(f[k]==k)return k;
    return f[k]=find(f[k]);
}

 3.接下来说下合并,举个例子,这就相当于俩组织合并,乙方老大拜入甲方老大门下,至此,俩组织合为一起(先依次各自找老大,如果老大不一样,则拜入甲方老大门下)

void union(int x,int y)                     
{
    int fx=find(x), fy=find(y);            
    if(fx != fy)                           
        f[fx]=fy;                       
}
                        

 代码示例

#include
using namespace std;
int i,j,k,n,m,s,ans,f[10010],p1,p2,p3;
//f[i]表示i的集合名
int find(int k){
	//路径压缩
    if(f[k]==k)return k;
    return f[k]=find(f[k]);
}
int main()
{
    cin>>n>>m;
    for(i=1;i<=n;i++)
        f[i]=i;//初始化i的老大为自己
    for(i=1;i<=m;i++){
        cin>>p1>>p2>>p3;
        if(p1==1)
            f[find(p2)]=find(p3);
            //拜入甲方老大门下
        else
            if(find(p2)==find(p3))//老大是同一个人
            
                printf("Y\n");
            else
                printf("N\n");
    }
    return 0;
}

以上如有错误,还望大佬指教

你可能感兴趣的:(学习,动态规划,算法)