数据结构1 题解

题目列表


A.并查集
B.带权并查集
C.最短路
D.二分
E.搜索
F.树搜
G.差分
H.离散化
I.离散化+差分
J.LCS
K.0-1背包

A.并查集

一、题目
[问题描述]
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
[基本要求]
Input:测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
Output:对每个测试用例,在1行里输出最少还需要建设的道路数目。
二、算法设计
1.主要思想:并查集模板题。并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。其主要思想是用集合中的一个元素代表集合。它管理一系列不相交的集合,并支持两种操作:
合并(Merge):把两个不相交的集合合并为一个集合。
查询(Find):查询两个元素是否在同一个集合中。
本题有3个子函数,init()、Find()、Merge()。
初始化init():用数组fa[]来存储每个元素的父结点,初始每个结点的父结点即是本身,即fa[i]=i。如图1.1所示。
在这里插入图片描述图1.1 初始化函数
查询Find():因为并查集是树型结构,若要寻找集合的代表元素,只需要一层一层往上访问父结点,直达树的根结点即可。(注:本题没有使用路径压缩,是最简单的并查集。)如图1.2所示。
在这里插入图片描述图1.2 查询函数
合并Merge():合并操作需要先找到两个集合的代表元素,然后将前者的父结点设为后者即可。(将后者的父节点设为前结点也行)如图1.3所示。
在这里插入图片描述图1.3 合并函数
注意:因为本题数据范围较小,所有没有使用路径压缩。若是大范围数据,则需路径压缩。如图1.4所示,路径压缩只需在查询的过程中,把沿途的每个结点的父结点都设为根节点。此时并查集的时间复杂度已经比较小了,但若有更加严格的数据还需进一步优化,如按秩合并等。
在这里插入图片描述

图1.4 路径压缩
三、调试分析
掌握并查集原理后,可直接上模板,没有卡壳的地方,调试运行比较顺利。
四、参考代码

#include<iostream>
#include<cstdio>
using namespace std;
int fa[1010];
int n,m;
void init()
{
    for(int i=1;i<=n;i++)//从1开始
        fa[i] = i;
}
int Find(int x)
{
    int r=x;
    while(fa[r]!=r)
        r=fa[r];
    return r;
}
void Merge(int x,int y)
{
    int fx,fy;
    fx = Find(x);
    fy = Find(y);
    if(fx != fy)
      fa[fx]=fy;
}
int main()
{
    int x,y,cnt=0;
    while(scanf("%d",&n),n)
    {
        init();
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d %d",&x,&y);
            Merge(x,y);
        }
        cnt=0;
        for(int i=1;i<=n;i++)
            if(fa[i]==i)
                cnt++;
        printf("%d\n",cnt-1);
    }
    return 0;
}

B.带权并查集

一、题目
[问题描述]
小Hi的学校总共有N名学生,编号1-N。学校刚刚进行了一场全校的古诗文水平测验。
学校没有公布测验的成绩,所以小Hi只能得到一些小道消息,例如X号同学的分数比Y号同学的分数高S分。
小Hi想知道利用这些消息,能不能判断出某两位同学之间的分数高低?
[基本要求]
第一行包含三个整数N, M和Q。N表示学生总数,M表示小Hi知道消息的总数,Q表示小Hi想询问的数量。
以下M行每行三个整数,X, Y和S。表示X号同学的分数比Y号同学的分数高S分。
以下Q行每行两个整数,X和Y。表示小Hi想知道X号同学的分数比Y号同学的分数高几分。
对于50%的数据,1 <= N, M, Q <= 1000
对于100%的数据,1 <= N, M, Q<= 100000 1 <= X, Y <= N -1000 <= S <= 1000
数据保证没有矛盾。
二、算法设计
1、主要思想:
1-1因为之前看过带权并查集类的问题,所以可以想到是带权并查集的模板题。但如果之前没做过也可以根据并查集的性质进行联想。因为并查集的基本应用就是处理集合问题。并查集的合并和查询优化,实际上是在改变树的形状。把原来“细长”的、操作低效的大量“小树”,变成了“粗短”的、操作高效的少量“大树”。如果在原来的“小树”上,点之间有权值,那么经过并查集的优化变成“大树”后,这些权值的操作也变得高效了。
注意:在本题中路径压缩代码与A题不同(因为要维护各结点到根结点的权值dis)
1-2定义一个权值数组dis[],结点i到父结点的权值为记为dis[i]。
1-3
查询函数
:路径压缩同A题类似,但需要原来的dis[x](点x到其父结点的权值),经过路径压缩后,x直接指向根节点,dis[x]也更新为x到根结点的权值,这是通过递归实现的。代码中,先用t记录x的原父结点;在递归过程中,最后返回的是根结点;最后将当前结点的权值加上原父结点的权值(注意:经过递归,此时父结点也直接指向根节点,父结点的权值也已经更新为父结点直接到根结点的权值了),就得到当前节点到根节点的权值。代码如图2.1所示。
在这里插入图片描述

图2.1 查询函数
1-4合并操作:合并x,y,将x的根结点fax接到到y的根结点fay上,并更新fax与fay间的权值,原理如图2.2、2.3所示,代码如图2.4所示。
在这里插入图片描述
图2.2 原理图1
在这里插入图片描述

图2.3 原理图2
在这里插入图片描述
图2.4 合并操作
三、调试分析
本地运行没有问题,不知道为什么在voj上一直报编译错误。拿标程投一遍也是报编译错误,应该是voj出了点问题。
四、参考代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
#define ll long long
const int N=100007;
int fa[N],dis[N];
int Find(int x)
{
    if(x!=fa[x])
    {
        int t=fa[x];         //记录父结点
        fa[x]=Find( fa[x]);  //路径压缩,递归最后返回的是根结点
        dis[x]+=dis[t];      //权值更新为x到根节点的权值
    }
    return fa[x];
}
int main()
{
    int n,m,q,x,y,s,fx,fy;
    cin>>n>>m>>q;
        for(int i=0;i<=n;i++){
           fa[i]=i;
           dis[i]=0;
        }
        for(int i=1;i<=m;i++)   //合并x,y
        {
            scanf("%d%d%d",&x,&y,&s);
            fax=Find(x);
            fay=Find(y);
            if(fx!=fy)
            {
                fa[fax]=fay;
                dis[fax]=dis[y]+s-dis[x];//更新权值
            }
        }
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d",&x,&y);
            fax=Find(x);
            fay=Find(y);
            if(fax!=fay){
                cout<<-1<<endl;
            }
            else{
                cout<<dis[x]-dis[y]<<endl;
            }
        }
    return 0;
}

C.最短路

一、题目
[问题描述]
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?
[基本要求]
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
二、算法分析
1、主要思想:用弗洛伊德算法或迪杰斯特拉算法解决单源最短路径问题,模板题,牢记以上两种算法即可。
三、调试分析
模板题直接过了,没有卡壳的地方。
四、参考代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
const int MAXC=10007;
int d[107][107],n,m;
void Floyd()
{
    for (int k=1;k<=n;k++)
    {
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=n;j++)
            {
                if (d[i][k]+d[k][j]<d[i][j])
                    d[i][j]=d[i][k]+d[k][j];
            }
        }
    }
}
int main()
{
    int a,b,c;
    while(scanf("%d%d",&n,&m)&&(n!=0||m!=0))
    {
        memset(d,MAXC,sizeof(d));
        for (int i=0;i<m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            d[a][b]=d[b][a]=c;
        }
        Floyd();
        printf("%d\n",d[1][n]);
    }
    return 0;
}

D.二分

一、题目
[问题描述]
Farmer John has built a new long barn, with N (2 <= N <= 100,000) stalls. The stalls are located along a straight line at positions x1,…,xN (0 <= xi <= 1,000,000,000).
His C (2 <= C <= N) cows don’t like this barn layout and become aggressive towards each other once put into a stall. To prevent the cows from hurting each other, FJ want to assign the cows to the stalls, such that the minimum distance between any two of them is as large as possible. What is the largest minimum distance?
[基本要求]
Input:

  • Line 1: Two space-separated integers: N and C
  • Lines 2…N+1: Line i+1 contains an integer stall location, xi
    Output:
  • Line 1: One integer: the largest minimum distance
    Ps:题目大意:农夫有c头牛,n个隔间,c头牛很容易相互打架,因此农夫想把它们分得越远越好,要你分配隔间使得相邻两头牛的距离越远越好(但要求最小距离差最大),问你这c头牛分割的最小距离的最大值。
    二、算法设计
    1.主要思想:由于编号不是按序输入,所以先对隔间的座标排序。对于两头牛,最小距离是0,最大距离不会超过两端两头牛的距离值。因此二分查找分割距离的最大值,每次mid都判断一次,判断的时候贪心地放置牛,保证前i头牛是符合这样分割标准的。
    Ps:本题一旦明白题意和结合数据范围后就能很快发现可以用二分来确定两头牛之间的距离d。
    三、调试运行
    去年做过这道题,第一次做这道题时没有理解题意导致没有做出来,后续看了题解才明白。然后又在周赛里做过一次,所以印象比较深刻,记得比较牢,调试分析没有太大问题。
    四、参考代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
int d[100010];
int l,r,n,m;
const int maxc=1000000000;
bool judge(int mid)
{
    int count=1;
    int last=d[0];
    for(int i=1;i<n;i++){
        if(d[i]-last>=mid){ //判断这样的牛间距是否符合现实条件
            count++;
            last=d[i];
        }
    }
    return count>=m;
}
int main()
{
    while(~scanf("%d%d",&n,&m)){
       for(int i=0;i<n;i++){
        scanf("%d",&d[i]);
       }
       sort(d,d+n);
       l=0,r=maxc;
       int count=-1;
       while(l<=r){
        int mid=(l+r)>>1;
        if(judge(mid)){
            count=mid;
            l=mid+1;//所有牛均能塞进牛棚,牛间距还可以再变大
        }else{
           r=mid-1;//牛间距只能减小,否则不能将所有牛都塞进牛棚
        }
       }
       printf("%d\n",count);
    }
    return 0;
}

E.搜索

一、题目
[问题描述]
Pass a year learning in Hangzhou, yifenfei arrival hometown Ningbo at finally. Leave Ningbo one year, yifenfei have many people to meet. Especially a good friend Merceki.
Yifenfei’s home is at the countryside, but Merceki’s home is in the center of city. So yifenfei made arrangements with Merceki to meet at a KFC. There are many KFC in Ningbo, they want to choose one that let the total time to it be most smallest.
Now give you a Ningbo map, Both yifenfei and Merceki can move up, down ,left, right to the adjacent road by cost 11 minutes.
Input
The input contains multiple test cases.
Each test case include, first two integers n, m. (2<=n,m<=200).
Next n lines, each line included m character.
‘Y’ express yifenfei initial position.
‘M’ express Merceki initial position.
‘#’ forbid road;
‘.’ Road.
‘@’ KCF
Output
For each test case output the minimum total time that both yifenfei and Merceki to arrival one of KFC.You may sure there is always have a KFC that can let them meet.
Ps:题目大意:两个人从两个地方出发,求到达同一个KFC所花费的最短时间。
二、算法分析
1.主要思想:两遍bfs,分别记录两人单独到达各个KFC店所花费的时间,然后再枚举两人到达同一KFC店的情况,记录花费的最短时间。
三、调试分析
做题前不久重温了up主(麦克老师讲算法)关于bfs,dfs这一块算法的视频,做起来没什么大问题。
四、参考代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define N 210
int dir[4][2]={0,1,1,0,0,-1,-1,0};
int n,m,MAXC;
char map1[N][N];
int visit[N][N];
int step1[N][N],step2[N][N];
struct node
{
	int x,y;
}st1,st2,s,t;
queue<node> q;
void Init()
{
	memset(visit,false,sizeof(visit));
	MAXC=INF;
	memset(step1,false,sizeof(step1));
	memset(step2,false,sizeof(step2));
}
void BFS(node st,int step[][210])
{
	q.push(st);
	visit[st.x][st.y]=true;
	while(!q.empty())
	{
		s=q.front();
		q.pop();
		for(int i=0;i<4;i++)
		{
			t.x=s.x+dir[i][0];
			t.y=s.y+dir[i][1];
			if(t.x>=0&&t.x<n&&t.y>=0&&t.y<m&&map1[t.x][t.y]!='#'&&!visit[t.x][t.y])
			{
				step[t.x][t.y]=step[s.x][s.y]+1;
				visit[t.x][t.y]=true;
				q.push(t);
			}
		}
	}
}
int main()
{
	int i,j;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		for(i=0;i<n;i++)
		{
			scanf("%s",map1[i]);
			for(j=0;j<m;j++)
			{
				if(map1[i][j]=='Y') st1.x=i,st1.y=j;
				if(map1[i][j]=='M') st2.x=i,st2.y=j;
			}
		}
		Init();
		BFS(st1,step1);
		memset(visit,false,sizeof(visit));
		BFS(st2,step2);
		for(i=0;i<n;i++)
			for(j=0;j<m;j++)
				if(map1[i][j]=='@'&&step1[i][j]!=0&&step2[i][j]!=0)
					if(step1[i][j]+step2[i][j]<MAXC) MAXC=step1[i][j]+step2[i][j];
		printf("%d\n",11*MAXC);
	}
	return 0;
}

F.树搜

一、题目
[问题描述]
There are n cities and n - 1 roads in the Seven Kingdoms, each road connects two cities and we can reach any city from any other by the roads.
Theon and Yara Greyjoy are on a horse in the first city, they are starting traveling through the roads. But the weather is foggy, so they can’t see where the horse brings them. When the horse reaches a city (including the first one), it goes to one of the cities connected to the current city. But it is a strange horse, it only goes to cities in which they weren’t before. In each such city, the horse goes with equal probabilities and it stops when there are no such cities.
Let the length of each road be 1. The journey starts in the city 1. What is the expected length (expected value of length) of their journey? You can read about expected (average) value by the link
[基本要求]
Input
The first line contains a single integer n (1 ≤ n ≤ 100000) — number of cities.
Then n - 1 lines follow. The i-th line of these lines contains two integers ui and vi (1 ≤ ui, vi ≤ n, ui ≠ vi) — the cities connected by the i-th road.
It is guaranteed that one can reach any city from any other by the roads.
Output
Print a number — the expected length of their journey. The journey starts in the city 1.
Your answer will be considered correct if its absolute or relative error does not exceed 10 - 6.
Namely: let’s assume that your answer is a, and the answer of the jury is b. The checker program will consider your answer correct, if .
Ps:题目大意:给一棵含有n个结点的树(无向图且无环),从根结点走向叶子结点,每条边权值为1且每个结点到其儿子结点的概率是一样的。问从根结点到所有叶子结点的期望距离之和。
二、算法分析
1.主要思想:由于是无向图且无环,只需考虑不经过其父结点即可,无需标记走过的路。然后dfs各支路长度和各结点的儿子结点数计算期望值。由于最初搞错了期望的含义,导致WA了两发,英文体面确实伤脑筋。(期望距离=该边距离*走该边的概率)
三、心得体会
由于最初搞错了期望的含义,导致WA了两发,英文体面确实伤脑筋,也从侧面反映英文水平有待提高。
四、参考代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
#define ll long long
const int N=2e5+7;
const int INF = 0x3f3f3f3f;
vector<int> G[N];
int n;
double dfs(int x, int father)
{
    double sum = 0.0, k = 0.0;
    int flag =0;
    for(int i = 0; i < G[x].size(); i++) {
        if(G[x][i] == father)
            continue;
        k+=1.0;
        flag =1;
        sum += dfs(G[x][i],x);
    }
    if(flag)
        return 1.0+sum/k;
    return 0.0;
}
int main()
{
    scanf("%d",&n);
    int x, y;
    for(int i = 1; i <=n-1; i++) {
        scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    printf("%.15lf\n",dfs(1,0));
    return 0;
}

G.差分

一、题目
[问题描述]
N个气球排成一排,从左到右依次编号为1,2,3…N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?
[基本要求]
每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N)。
当N = 0,输入结束。
每个测试实例输出一行,包括N个整数,第I个数代表第I个气球总共被涂色的次数。
二、算法分析
1.主要思想:经典差分模板题。设所有气球初始涂色数为0。先给L到n这段区间的气球涂一个颜色,然后将R+1到n这段区间的气球颜色消去一个,即从a[L]开始,所有气球涂色数+1,从a[R+1]开始,所有气球涂色数-1,即a[L]++,a[R+1]–。最后,第i个气球的涂色数数即为a[i] = a[1]+a[2]+……+a[i-1]。使用for循环,即涂色数数即为a[i] +=a[i-1]。
三、调试运行
经典差分题,比较简单,没有出现问题。
四、参考代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 100007
int l,r,n,m;
int a[maxn];
int main()
{
    while(~scanf("%d",&n)!=EOF)
    {
        if(n==0)break;
        for(int i=1;i<=n;i++)
            a[i]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&l,&r);
            a[l]++;
            a[r+1]--;
        }
        for(int i=1;i<=n;i++)
        {
            if(i==1)printf("%d",a[1]);
            else
            {
                a[i]+=a[i-1];//优化了一下,边计算边输出
                printf(" %d", a[i]);
            }
        }
        printf("\n");
    }
    return 0;
}

H.离散化

一、题目
[问题描述]
Moscow is hosting a major international conference, which is attended by n scientists from different countries. Each of the scientists knows exactly one language. For convenience, we enumerate all languages of the world with integers from 1 to 109.
In the evening after the conference, all n scientists decided to go to the cinema. There are m movies in the cinema they came to. Each of the movies is characterized by two distinct numbers — the index of audio language and the index of subtitles language. The scientist, who came to the movie, will be very pleased if he knows the audio language of the movie, will be almost satisfied if he knows the language of subtitles and will be not satisfied if he does not know neither one nor the other (note that the audio language and the subtitles language for each movie are always different).
Scientists decided to go together to the same movie. You have to help them choose the movie, such that the number of very pleased scientists is maximum possible. If there are several such movies, select among them one that will maximize the number of almost satisfied scientists.
[基本要求]
Input
The first line of the input contains a positive integer n (1 ≤ n ≤ 200 000) — the number of scientists.
The second line contains n positive integers a1, a2, …, an (1 ≤ ai ≤ 109), where ai is the index of a language, which the i-th scientist knows.
The third line contains a positive integer m (1 ≤ m ≤ 200 000) — the number of movies in the cinema.
The fourth line contains m positive integers b1, b2, …, bm (1 ≤ bj ≤ 109), where bj is the index of the audio language of the j-th movie.
The fifth line contains m positive integers c1, c2, …, cm (1 ≤ cj ≤ 109), where cj is the index of subtitles language of the j-th movie.
It is guaranteed that audio languages and subtitles language are different for each movie, that is bj ≠ cj.
Output
Print the single integer — the index of a movie to which scientists should go. After viewing this movie the number of very pleased scientists should be maximum possible. If in the cinema there are several such movies, you need to choose among them one, after viewing which there will be the maximum possible number of almost satisfied scientists.
If there are several possible answers print any of them.
Ps:题目大意:
有 n 个人,每个人会一种语言.
有 m 个电影,每个电影有音频和字幕,音频种类和字幕种类不同.
如果一个人能听懂音频,他会非常高兴.
如果一个人能看懂字幕,他会比较高兴.
现要求选择一场电影,非常高兴的人数最多,如果解不唯一,就选择比较高兴人数最多的那场,输出该电影的编号。
二、算法设计
1.主要思想:从题目中发现a、b、c是大范围数据,直接开数组肯定会发生段错误,但我们发现总数n的范围(1 ≤ n ≤ 200 000) 可以接受,所以用map存储数据,否则会WA。
2.先在所有电影中,找出配音语言有最多人会的电影。但这种电影可能有多部,于是再在这些电影中找出字幕语言有最多人会的电影即可。
三、调试运行
提交代码时,错将之前保留的老版本代码提交了,导致WA了一发,以后提交代码时需留心。
四、参考代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
const int N=200007;
int n,m;
map<int,int>scientists;
struct cinema{
    int py,zm;
}movie[N];
int main(){
    int k,res=0,maxc1=-1,maxc2=-1;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&k);
        scientists[k]++;//统计说每种语言的专家数量
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
        scanf("%d",&movie[i].py);
    for(int i=1;i<=m;i++)
        scanf("%d",&movie[i].zm);
    for(int i=1;i<=m;i++){
        if(scientists[movie[i].py]>maxc1){
            maxc1=scientists[movie[i].py];
            maxc2=scientists[movie[i].zm];
            res=i;
        }
        else if(scientists[movie[i].py]==maxc1){
            if(scientists[movie[i].zm]>maxc2){
                maxc2=scientists[movie[i].zm];
                res=i;
            }
        }
    }
    printf("%d\n",res);
    return 0;
}

I.离散化+差分

一、题目
[基本要求]
There are N N N light bulbs indexed from 0 0 0 to N − 1 N-1 N1. Initially, all of them are off.
A FLIP operation switches the state of a contiguous subset of bulbs. F L I P ( L , R ) FLIP(L, R) FLIP(L,R) means to flip all bulbs x x x such that L ≤ x ≤ R L \leq x \leq R LxR. So for example, F L I P ( 3 , 5 ) FLIP(3, 5) FLIP(3,5) means to flip bulbs 3 3 3 , 4 4 4 and 5 5 5, and F L I P ( 5 , 5 ) FLIP(5, 5) FLIP(5,5) means to flip bulb 5 5 5.
Given the value of N N N and a sequence of M M M flips, count the number of light bulbs that will be on at the end state.
InputFile
The first line of the input gives the number of test cases, T T T. T T T test cases follow. Each test case starts with a line containing two integers N N N and M M M, the number of light bulbs and the number of operations, respectively. Then, there are M M M more lines, the i i i-th of which contains the two integers L i L_i Li and R i R_i Ri, indicating that the i i i-th operation would like to flip all the bulbs from L i L_i Li to R i R_i Ri , inclusive.
1 ≤ T ≤ 1000 1 \leq T \leq 1000 1T1000
1 ≤ N ≤ 1 0 6 1 \leq N \leq 10^6 1N106
1 ≤ M ≤ 1000 1 \leq M \leq 1000 1M1000
0 ≤ L i ≤ R i ≤ N − 1 0 \leq L_i \leq R_i \leq N-1 0LiRiN1
OutputFile
For each test case, output one line containing Case #x: y, where x x x is the test case number (starting from 1 1 1) and y y y is the number of light bulbs that will be on at the end state, as described above.
Ps:题目大意:现在有n盏灯,对这n盏灯进行m次操作,每次操作给一个区间,对这个区间的灯进行相应操作。如果这盏灯是开着的就将其关掉,如果是关着的就将其打开,最后问m次操作之后开着的灯的数量。
二、算法分析
1.主要思想:最初以为还是差分模板题,算算时间复杂度应该也能过,结果段错误一发,即卡时间也卡内存。本题我觉得应该是考思维,既然大范围数据n过不了,只能从m的范围着手,只需要考虑哪些有贡献的2*m个点就行了,对于区间中那些为0的部分,无需遍历,重复的点也无需特殊处理。
三、心得体会
最初多次被卡时间和空间,没有想到只需存端点加端点排序就行,看了题解才明白,以后思维还有待提高。
四、参考代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define N 2007
int l,r,n,m,cnt,k,T;
int a[N];
int main()
{
    scanf("%d",&T);
    k=1;
    while(k<=T)
    {
        cnt=0;
        int t=0;
        scanf("%d%d",&n,&m);
        for(int i=0;i<N;i++)
            a[i]=0;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&l,&r);
            a[t++]=l;
            a[t++]=r+1;
        }
        sort(a,a+2*m);
        for(int i=0;i<t;i+=2)
        {
            cnt+=a[i+1]-a[i];
        }
         printf("Case #%d: %d\n",k,cnt);
         k++;
    }
    return 0;
}

J.LCS

一、题目
[问题描述]
给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的)。
比如两个串为:

abcicba
abdkscab

ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最长的子序列。
[基本要求]
Input
第1行:字符串A 第2行:字符串B (A,B的长度 <= 1000)
Output
输出最长的子序列,如果有多个,随意输出1个。
二、算法设计
1.主要思想:经典LCS问题,理解分析后可用模板,详细推理过程链接如下:https://www.icourse163.org/learn/BUAA-1449777166?tid=1461147444#/learn/content?type=detail&id=1237474293&cid=1257390614
2.推导公式如图1.1所示。

在这里插入图片描述
图1.1 推导公式
三、心得体会
大一学LCS的时候很费力,搞不懂怎么推理和计算,看视频也不是特别懂。这学期寒假在童咏昕老师开设的《算法设计与分析》课程里又看见了LCS问题的讲解,讲得非常详细,这让我对最长公共子序列问题有了更深刻的理解。
四、参考代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
int dp[1007][1007];
char a1[1007],a2[1007],a3[1007],l[1007];
int main()
{
    cin>>a1>>a2;
    int len1,len2;
    len1=strlen(a1);
    len2=strlen(a2);
    for(int i=1;i<=len1;i++){
        for(int j=1;j<=len2;j++){
            if(a1[i-1]==a2[j-1])
                dp[i][j]=dp[i-1][j-1]+1;
            else{
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
    int len3=dp[len1][len2];
    l[len3]='\0';
    int i=len1,j=len2;
    while(dp[i][j]){
        if(dp[i][j]==dp[i-1][j])
            i--;
        else if(dp[i][j]==dp[i][j-1]){
            j--;
        }else
        {
            l[--len3]=a1[i-1];
            i--;
            j--;
        }
    }
    cout<<l<<endl;
    return 0;
}

K.背包问题

一、题目
[问题描述]
将一堆正整数分为2组,要求2组的和相差最小。
例如:1 2 3 4 5,将1 2 4分为1组,3 5分为1组,两组和相差1,是所有方案中相差最少的。
[基本要求]
Input
第1行:一个数N,N为正整数的数量。 第2 - N+1行,N个正整数。 (N <= 100, 所有正整数的和 <= 10000)
Output
输出这个最小差
二、算法分析
1.主要思想:本题为0-1背包问题。0-1背包问题:在最优解中,每个物品只有两种可能的情况,即在背包中或者不在背包中(背包中的该物品数为0或1),因此称为0-1背包问题。
2.找子问题:子问题必然是和物品有关的,对于每一个物品,有两种结果:能装下或者不能装下。第一,包的容量比物品体积小,装不下,这时的最大价值和前i-1个物品的最大价值是一样的。第二,还有足够的容量装下该物品,但是装了不一定大于当前相同体积的最优价值,所以要进行比较。由上述分析,子问题中物品数和背包容量都应当作为变量。因此子问题确定为背包容量为j时,求前i个物品所能达到最大价值。
3.确定状态:由上述分析,“状态”对应的“值”即为背包容量为j时,求前i个物品所能达到最大价值,设为dp[i][j]。初始时,dp[0]j为0,没有物品也就没有价值。
4.确定状态转移方程:由上述分析,第i个物品的体积为w,价值为v,则状态转移方程为
5.j<w,dp[i][j] = dp[i-1][j] //背包装不下该物品,最大价值不变
6.j>=w, dp[i][j] = max{ dp[i-1][j-list[i].w] + v, dp[i-1][j] } //和不放入该物品时同样达到该体积的最大价值比较
7.算法优化:观察状态转移方程的特点,我们发现dp[i][j]的转移只与dp[i-1][j-list[i].w]和dp[i-1][j]有关,即仅与二维数组本行的上一行有关。因此,我们可以将二维数组优化为一维数组。不过这里要注意两点:1.j<w的状态转移方程不再需要了。2.为保证每个物品只能使用一次,我们倒序遍历所有j的值,这样在更新dp[j]的时候,dp[j-list[i].w]的值尚未被修改,就不会出现一个物品重复使用的问题。
优化后的状态转移方程:dp[j] = max{ dp[j-list[i].w] + v, dp[j] }
8.本题中物品的价值与重量相等均为v[i]。
9.本题需要尽可能的均分为sum/2,但是不能用贪心。相当于给了一个sum/2的背包,问能够装的最大值,再计算两组数的差值。
三、心得体会
很久没做相关背包题,有些知识点本就不易理解又加上不常复习,容易忘,导致不能灵活运用,遇到一道题需要花费很长时间才能想起来它的模型。自己还是属于没有理解深刻,平时还需多加强知识的练习与回顾。
四、参考代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
const int N=10007;
int v[107],dp[N];
int main()
{
    int n,sum=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&v[i]);
        sum+=v[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=sum/2;j>=v[i];j--){
            dp[j]=max(dp[j],v[i]+dp[j-v[i]]);
        }
    }
    printf("%d\n",abs((sum-dp[sum/2])-dp[sum/2]));
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值