11.15——11.21ACM笔记

P1160 队列安排
一个学校里老师要将班上NN个同学排成一列,同学被编号为1\sim N1∼N,他采取如下的方法:

先将11号同学安排进队列,这时队列中只有他一个人;

2-N2−N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1\sim (i -1)1∼(i−1)中某位同学(即之前已经入列的同学)的左边或右边;

从队列中去掉M(M<N)M(M<N)个同学,其他同学位置顺序不变。

在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。

#include <bits/stdc++.h>
struct node{
    int n;
    node *left,*right;
    node(int t){
        left=right=NULL;
        n=t;
    }
}*p[100010],*q;
int main(){
    int m,n,i,j,k,u=1,v;
    p[1]=new node(1);
    scanf("%d",&n);
    for(i=2;i<=n;i++){
        scanf("%d%d",&j,&k);
        p[i]=new node(i);
        if(k){
            if(p[j]->right){
                p[j]->right->left=p[i];
                p[i]->right=p[j]->right;    
            }
            p[j]->right=p[i];
            p[i]->left=p[j];
        }
        else{
            if(p[j]->left){
                p[j]->left->right=p[i];
                p[i]->left=p[j]->left;    
            }
            p[j]->left=p[i];
            p[i]->right=p[j];
            if(j==u)u=i;
        }
    }
    scanf("%d",&m);
    for(i=1;i<=m;i++){
        scanf("%d",&k);
        if(p[k]->left)
            p[k]->left->right=p[k]->right;
        if(p[k]->right){
            p[k]->right->left=p[k]->left;
            if(k==u)
                u=p[k]->right->n;
        }
        p[k]->left=p[k]->right=NULL;
    }
    q=p[u];
    while(q){
        printf("%d ",q->n);
        q=q->right;
    }
    return 0;
}

难得碰到一个链表的题,正好试试手,练一下数据结构的知识。
P1122 最大子树和
小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉的问题。于是当日课后,小明就向老师提出了这个问题:

一株奇怪的花卉,上面共连有NN朵花,共有N-1N−1条枝干将花儿连在一起,并且未修剪时每朵花都不是孤立的。每朵花都有一个“美丽指数”,该数越大说明这朵花越漂亮,也有“美丽指数”为负数的,说明这朵花看着都让人恶心。所谓“修剪”,意为:去掉其中的一条枝条,这样一株花就成了两株,扔掉其中一株。经过一系列“修剪“之后,还剩下最后一株花(也可能是一朵)。老师的任务就是:通过一系列“修剪”(也可以什么“修剪”都不进行),使剩下的那株(那朵)花卉上所有花朵的“美丽指数”之和最大。

老师想了一会儿,给出了正解。小明见问题被轻易攻破,相当不爽,于是又拿来问你。

#include <bits/stdc++.h>
using namespace std;
bool f[16002][16002]={false};
int w[16002][2]={0},i,a,b,o=0,n;

int sum(int a){
	int j;
	if(w[a][2]==0){
		if(w[a][1]<0)return 0;
		else return w[a][1];
	}
	for(j=3;j<=n+2;j++)if(f[a][j]){
		f[a][j]=false;f[j-2][a+2]=false;
	w[a][1]=w[a][1]+sum(j-2);
	if(w[a][1]>o)o=w[a][1];
	}

	if(w[a][1]<0)return 0;
		else return w[a][1];
}

int main ()
{
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&w[i][1]);
}
for(i=1;i<=n-1;i++)
{
	scanf("%d%d",&a,&b);
	w[a][2]++;
	w[b][2]++;
	f[b][a+2]=true;
	f[a][b+2]=true;
}
sum(1);
printf("%d",o);
return 0;

}

子树问题,还是数据结构的知识,W[1][1]是第一个点的值,W[1][2]是和这个点连接的数量,后面是记录和这个点连接的点。
f数组是记录两个点是否连接。f[x][y]是x点是否能到y点,如果x能到y点那么y点也能到x点,所以f[y][x]也是打开的。
然后就开始深搜。深搜的时候要记得把f关闭,不然会死循环!!!而且双向路都要关闭!!!
搜到最底下就把这个点的值递回来,然后加上沿路所有点的值。
P1140 相似基因
大家都知道,基因可以看作一个碱基对序列。它包含了44种核苷酸,简记作A,C,G,TA,C,G,T。生物学家正致力于寻找人类基因的功能,以利用于诊断疾病和发明药物。

在一个人类基因工作组的任务中,生物学家研究的是:两个基因的相似程度。因为这个研究对疾病的治疗有着非同寻常的作用。

#include<bits/stdc++.h>
using namespace std;
int la,lb,a[110],b[110],f[110][110];
int d[6][6]=
{
	{0,0,0,0,0,0},
	{0,5,-1,-2,-1,-3},
	{0,-1,5,-3,-2,-4},
	{0,-2,-3,5,-2,-2},
	{0,-1,-2,-2,5,-1},
	{0,-3,-4,-2,-1,0}
};
int main()
{
	
	cin>>la;
	for(int i=1;i<=la;i++)
	{
		char t;
		cin>>t;
		switch(t)
		{
		case'A':
			a[i]=1;break;
		case'C':
			a[i]=2;break;
		case'G':
			a[i]=3;break;
		case'T':
			a[i]=4;break;
		}
	}
	cin>>lb;
	for(int i=1;i<=lb;i++)
	{
		char t;
		cin>>t;
		switch(t)
		{
		case'A':
			b[i]=1;break;
		case'C':
			b[i]=2;break;
		case'G':
			b[i]=3;break;
		case'T':
			b[i]=4;break;
		}
	}

	f[0][0]=0;
	for(int i=1;i<=la;i++)
		f[i][0]=f[i-1][0]+d[a[i]][5];
	for(int i=1;i<=lb;i++)
		f[0][i]=f[0][i-1]+d[5][b[i]];
	for(int i=1;i<=la;i++)
		for(int j=1;j<=lb;j++)
			f[i][j]=max(f[i-1][j-1]+d[a[i]][b[j]],max(f[i-1][j]+d[a[i]][5],f[i][j-1]+d[5][b[j]]));
	cout<<f[la][lb]<<endl;
	return 0;
}

动态规划题。
若在第一段基因中插入空碱基相似度更优 则在第一段插入空碱基
若在第二段基因中插入空碱基相似度更优 则在第二段插入空碱基
若直接配对的相似度高于以上两者 则直接插入。
dp[i][j]=max(dp[i-1][j-1]+(i与j的匹配值),dp[i-1][j]+(i与空的匹配值),dp[i][j-1]+(j与空的匹配值)。

这周没做太多题,有些难题试了很多次没做出来,只能再回来找些适合自己的题继续练,比赛的题也看了,详解的思路都很清晰,看了几遍感觉还是有距离。

上一篇:[ACM] CF水题记


下一篇:实验三