连续背包 (背包套背包)

连续背包(bag

【问题描述】

从T组物品中选出一些物品,放入背包中,求剩余空间的最小值。

限制条件:从每组物品中挑选物品必须要选取连续的一段。就是说,如果这组物品共有n个: 物品1、物品2、物品3、…、物品n,那么只能选取物品i、物品i+1、…、物品j,其中1<=i<=j<=n,或者不选。

【输入】

第一行为两个用空格隔开的正整数v和T。表示背包的空间和物品的组数。

接下来有T行,每行先是一个正整数ni,表示这组物品有ni个,然后ni个正整数,表示每个物品的大小。

【输出】

 仅一个数,表示剩余空间的最小值。

【输入输出样例】

bag.in

100 3

3 7 6 8

2 80 70

4 101 108 103150

 

bag.out

6

 

【输入输出样例解释】

 第1组选6、8,第2组选80,第3组不选。

【限制】

60%的数据满足:1 <= ni <= 10

100%的数据满足:1 <= ni <= 100,1<=v<=5000,1<=T<=10

 

对每一段背包

再对各段背包

注意单步中Vmax=V[i-1]+sum[ni]

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXNI (100+10)
#define MAXV (500000+10)
#define MAXN (10+10)
int T,n,v,sum[MAXNI]={0},a[MAXNI],A[MAXN][MAXV]={0};
bool f[MAXN][MAXV]={0};
int main()
{
	freopen("bag.in","r",stdin);
	freopen("bag.out","w",stdout);
	scanf("%d%d",&v,&T);
//	v=v*10;
	f[0][0]=1;
	for (int i=1;i<=T;i++)
	{
		scanf("%d",&n);
		sum[0]=0;
		for (int j=1;j<=n;j++) {scanf("%d",&a[j]);if (a[j]>v/*/10*/) a[j]=0; sum[j]=sum[j-1]+a[j];}
		//for (int j=1;j<=n;j++) printf("%d ",sum[j]);
		for (int k=0;k<=v+sum[n];k++)
			for (int j=n;j>=0;j--)
			{
				if (k-sum[j]<0) continue;
				if (f[i-1][k-sum[j]])
				{
					f[i][k]=1;
					A[i][k]=j;
					break;
				}
			}
		for (int k=0;k<=v+sum[n];k++)
			for (int j=A[i][k]-1;j>=1;j--)
			{
				f[i][k-sum[j]]=f[i][k]||f[i][k-sum[j]];
			}



		//cout<<endl;
	}
//	v/=10;
	int i=v;
	while (!f[T][i])
	{
		i--;
	}
	printf("%dn",v-i);
/*
	for (int i=0;i<=T;i++)
	{
		for (int j=0;j<=v;j++)
			if (f[i][j]) cout<<i<<','<<j<<' ';
		cout<<'n';
	}

	while (1);
*/	return 0;
}

水灾 (BFS-先洪水后寻路)

水灾(sliker

大雨应经下了几天雨,却还是没有停的样子。ksy刚从外地回来,知道不久除了自己家,其他的地方都将会被洪水淹没。

ksy的老家可以用一个N*M的地图表示,地图上有五种符号:“. * X D S”。其中“X”表示石头,水和人都不能从上面经过。“.”表示平原,ksy和洪水都可以经过。“*”表示洪水开始地方(可能有多个地方开始发生洪水)。“D”表示ksy的家。“S”表示ksy现在的位置。

ksy每分钟可以向相邻位置移动,而洪水将会在ksy移动之后把相邻的没有的土地淹没(从已淹没的土地)。

求ksy回答家最少时间。如他回不了家,被淹死输出KAKTUS。

Input

3 3

D.*

...

.S.

Output

3

Input

3 3

D.*
...

..S

Output

KAKTUS

Input

3 6

D...*.

.X.X..

....S.

Output

6

因为第i秒走后,所到达的点不能有Flood

所以必须在之前Flood,然后再往下找

显然柯黑再同一个地方停留不优

故只要存储到达一个点的最短时间

注意C++中构造函数的写法

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<functional>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN (50+10)

struct node
{
	int x,y,t;
	node():x(0),y(0),t(0){}
	node(int _x,int _y,int _t):x(_x),y(_y),t(_t){/*cout<<x<<' '<<y<<' '<<t<<endl;*/}
}start,end;
/*
node made_node(int i,int j,int t)
{
	node now;
	now.x=i;
	now.y=j;
	now.t=t;
	return now;
}
*/

int n,m;
bool map[MAXN][MAXN],b[MAXN][MAXN];
char s[MAXN];
queue<node> flood,q;
bool inside(int x,int y)
{
	if (x>=1&&x<=n&&y>=1&&y<=m) return true;
	return false;
}
bool bfs()
{
	int l=-1;
	while (!q.empty())
	{
		node now=q.front();
//		cout<<now.x<<' '<<now.y<<endl;
		q.pop();
		if (now.t>l)
		{
			int size=flood.size();
			while (size)
			{
				node nowf=flood.front();
				flood.pop();
				int x=nowf.x,y=nowf.y;
				if (x>1&&b[x-1][y])
				{
					flood.push(node(x-1,y,now.t));
					map[x-1][y]=b[x-1][y]=0;
				}
				if (x<n&&b[x+1][y])
				{
					flood.push(node(x+1,y,now.t));
					map[x+1][y]=b[x+1][y]=0;
				}
				if (y>1&&b[x][y-1])
				{
					flood.push(node(x,y-1,now.t));
					map[x][y-1]=b[x][y-1]=0;
				}
				if (y<m&&b[x][y+1])
				{
					flood.push(node(x,y+1,now.t));
					map[x][y+1]=b[x][y+1]=0;
				}

				size--;
			}
			l++;
		}
		int x=now.x,y=now.y;
//		if (!map[x][y]) continue;
		if (x>1&&map[x-1][y])
		{
			if (x-1==end.x&&y==end.y){end.t=now.t+1; return true;}
			q.push(node(x-1,y,now.t+1));
			map[x-1][y]=0;
		}
		if (x<n&&map[x+1][y])
		{
			if (x+1==end.x&&y==end.y){end.t=now.t+1; return true;}
		 	q.push(node(x+1,y,now.t+1));
			map[x+1][y]=0;
		}
		if (y>1&&map[x][y-1])
		{
			if (x==end.x&&y-1==end.y){end.t=now.t+1; return true;}
			q.push(node(x,y-1,now.t+1));
			map[x][y-1]=0;
		}
		if (y<m&&map[x][y+1])
		{
			if (x==end.x&&y+1==end.y){end.t=now.t+1; return true;}
			q.push(node(x,y+1,now.t+1));
			map[x][y+1]=0;
		}





	}
	return false;
}

int main()
{
	freopen("sliker.in","r",stdin);
	freopen("sliker.out","w",stdout);
	scanf("%d%d",&n,&m);
	memset(map,1,sizeof(map));
	memset(b,1,sizeof(b));

	for (int i=1;i<=n;i++)
	{
		scanf("%s",s);
		for (int j=0;j<m;j++)
		{
			if (s[j]=='S')
			{
				start=node(i,j+1,0);
				q.push(start);
			}
			if (s[j]=='D')
			{
				end=node(i,j+1,0);
				b[i][j+1]=0;
			}
			if (s[j]=='X')
			{
				map[i][j+1]=0;
				b[i][j+1]=0;
			}
			if (s[j]=='*')
			{
				map[i][j+1]=0;
				b[i][j+1]=0;
				flood.push(node(i,j+1,0));
			}

		}
	}
/*	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			if (map[i][j]) cout<<"map "<<i<<' '<<j<<endl;
	cout<<"end"<<end.x<<' '<<end.y;
*/

	if (bfs()) printf("%dn",end.t);
	else printf("KAKTUSn");


//	while (1);
	return 0;
}

旅行 (分别考虑不同方向曼哈顿距离和)

第二题:旅行(journey)

时间限制:1秒

内存限制:256MB

输入:journey.in

输出:journey.out

问题描述

给定一个n行m列的字符矩阵,’.’代表空地,’X’代表障碍。移动的规则是:每秒钟以上下左右四个方向之一移动一格,不能进入障碍。计算:在空地中随机选择起点和终点(可以重合,此时最短耗时为0),从起点移动到终点最短耗时的平均值。

每一行每一列至多有1个障碍,并且障碍不在对角线方向相邻。以下矩阵是不合法的:

.X

X.

输入

第一行两个整数n, m。。

接下来n行,每行m个字符’.’或’X’。在50%的数据中,保证每个字符都是’.’。

输出

平均耗时,保留4位小数,四舍五入。数据保证在范围之内输出一致,Ans是标准答案。

样例输入

2 2

..

.X

样例输出

0.8889

 

 

 

 

 

yle�� sth�p�ive;top:3.0pt;mso-text-raise:-3.0pt'>。所有信息按照从左到右的顺序给出。在50%的数据中,。

输出

第一行:出列的总对数k。接下来输出k行,每行是两个整数。按跳舞顺序输出,两个整数代表这一对舞伴的编号(按输入顺序从左往右1至n编号)。请先输出较小的整数,再输出较大的整数。

样例输入

4

BGBG

4 2 4 3

样例输出

2

3 4

1 2

样例输入

4

BGBB

1 1 2 3

样例输出

1

1 2

解1:

Dp出所有平地曼哈顿距离和(全T)

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<functional>
#include<algorithm>
#include<stack>
using namespace std;
#define MAXN (1000+10)
#define DELTA (0.00000001)
#define F (10000000)
int n,m;
char s[2000];
int a[MAXN],b[MAXN];
long long f[MAXN][MAXN];
int calc(int l,int r,int n)
{
	return 2*(l-1)*(n-r);
}


int main()
{
	freopen("journey.in","r",stdin);
	freopen("journey.out","w",stdout);

/*	long long ans=0;
	scanf("%d%d",&n,&m);
	for (int n=1;n<=10;n++)
	{
	for (int m=1;m<=10;m++)
	{
	long long ans=0;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			for (int k=1;k<=n;k++)
				for (int l=1;l<=m;l++)
					ans+=abs(i-k)+abs(j-l);

	printf("%d ",ans);
	}
	cout<<endl;
}
*/

	memset(f,0,sizeof(f));
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));

	long long tot=0;
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%s",s);
		for (int j=0;j<m;j++)
			if (s[j]=='X')
			{
				tot++;
				a[i]=j+1;
				b[j+1]=i;
				break;
			}
	}
	tot=n*m-tot;

//	for (int i=1;i<=n;i++) cout<<a[i]<<' ';


	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
		{
			f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+i*j*(i+j-2)+(i-1)*(j-1)*(i+j);
			if (a[i]==j) f[i][j]-=+i*j*(i+j-2);
			else
			{
				for (int k=1;k<=i;k++)
				 if (a[k]&&a[k]<=j)
				 f[i][j]-=2*(i-k+j-a[k]);


				if (a[i]&&a[i]<j)
					 f[i][j]-=(i-1)*(i+2*(j-a[i]));
				if (b[j]&&b[j]<i)
					 f[i][j]-=(j-1)*(j+2*(i-b[j]));
				if (a[i]&&a[i]<j&&b[j]&&b[j]<i)
					f[i][j]+=2*((j-a[i])+(i-b[j]));

			}
//			cout<<f[i][j]<<endl;
		}
/*
	for (int i=0;i<=n;i++){
		for (int j=0;j<=m;j++)
			cout<<f[i][j]<<' ';
		cout<<'n';
	}*/

	long long ans=f[n][m];
//	cout<<ans<<endl;
	for (int i=1;i<=n;i++)
		if (a[i])
		{
			ans+=2*calc(a[i],a[i],m);
			if (i>1&&a[i-1])
			{
				int j=i-1;
				if (a[j]<a[j+1])
				{
					while (j>0&&a[j]&&a[j]<a[j+1]) {ans+=2*calc(a[j],a[i],m);j--;}
				}/*
				else if (a[j]>a[j+1])
				{
					while (j>0&&a[j]>a[j+1]) {ans+=2*calc(a[i],a[j],m);j--;}
				}	*/
			}
			if (i<n&&a[i+1])
			{
				int j=i+1;
				if (a[j]<a[j-1])
				{
					while (j<=n&&a[j]&&a[j]<a[j-1]) {ans+=2*calc(a[j],a[i],m);j++;}
				}/*
				else if (a[j]>a[j-1])
				{
					while (j<=n&&a[j]>a[j-1]) {ans+=2*calc(a[i],a[j],m);j++;}
				}*/
			}
		}
	for (int i=1;i<=m;i++)
		if (b[i])
		{
			ans+=2*calc(b[i],b[i],n);
			if (i>1&&b[i-1])
			{
				int j=i-1;
				if (b[j]<b[j+1])
				{
					while (j>0&&b[j]&&b[j]<b[j+1]) {ans+=2*calc(b[j],b[i],n);j--;}
				}/*
				else if (b[j]>b[j+1])
				{
					while (j>0&&b[j]>b[j+1]) {ans+=2*calc(b[i],b[j],n);j--;}
				}*/
			}
			if (i<m&&b[i+1])
			{
				int j=i+1;
				if (b[j]<b[j-1])
				{
					while (j<=m&&b[j]&&b[j]<b[j-1]) {ans+=2*calc(b[j],b[i],n);j++;}
				}/*
				else if (b[j]>b[j-1])
				{
					while (j<=m&&b[j]>b[j-1]) {ans+=2*calc(b[i],b[j],n);j++;}
				}*/
			}
		}
//	cout<<ans<<endl;
	double _ans=double(ans)/double(tot)/double(tot);
	printf("%.4lfn",_ans);


	while (1);

	return 0;


}

正解

找一格,向前后搜 曼哈顿距离和分别考虑纵横方向

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<functional>
#include<algorithm>
#include<stack>
using namespace std;
#define MAXN (1000+10)
#define DELTA (0.00000001)
#define F (10000000)
int n,m;
char s[2000];
int a[MAXN],b[MAXN];
//long long f[MAXN][MAXN];
int calc(int l,int r,int n)
{
	return 2*(l-1)*(n-r);
}


int main()
{
	freopen("journey.in","r",stdin);
	freopen("journey.out","w",stdout);

//	memset(f,0,sizeof(f));
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));

	long long tot=0;
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%s",s);
		for (int j=0;j<m;j++)
			if (s[j]=='X')
			{
				tot++;
				a[i]=j+1;
				b[j+1]=i;
				break;
			}
	}
	tot=n*m-tot;

//	for (int i=1;i<=n;i++) cout<<a[i]<<' ';

	/*
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
		{
			f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+i*j*(i+j-2)+(i-1)*(j-1)*(i+j);
			if (a[i]==j) f[i][j]-=+i*j*(i+j-2);
			else
			{
				for (int k=1;k<=i;k++)
				 if (a[k]&&a[k]<=j)
				 f[i][j]-=2*(i-k+j-a[k]);


				if (a[i]&&a[i]<j)
					 f[i][j]-=(i-1)*(i+2*(j-a[i]));
				if (b[j]&&b[j]<i)
					 f[i][j]-=(j-1)*(j+2*(i-b[j]));
				if (a[i]&&a[i]<j&&b[j]&&b[j]<i)
					f[i][j]+=2*((j-a[i])+(i-b[j]));

			}
//			cout<<f[i][j]<<endl;
		}
/*
	for (int i=0;i<=n;i++){
		for (int j=0;j<=m;j++)
			cout<<f[i][j]<<' ';
		cout<<'n';
	}*/

	long long ans=0;
	for (int i=1;i<n;i++)
		for (int j=i+1;j<=n;j++)
			ans+=(m-(a[i]>0))*(m-(a[j]>0))*(j-i);
	for (int i=1;i<m;i++)
		for (int j=i+1;j<=m;j++)
			ans+=(n-(b[i]>0))*(n-(b[j]>0))*(j-i);
	ans*=2;





//	long long ans=f[n][m];
//	cout<<ans<<endl;
	for (int i=1;i<=n;i++)
		if (a[i])
		{
			ans+=2*calc(a[i],a[i],m);
			if (i>1&&a[i-1])
			{
				int j=i-1;
				if (a[j]<a[j+1])
				{
					while (j>0&&a[j]&&a[j]<a[j+1]) {ans+=2*calc(a[j],a[i],m);j--;}
				}
			}
			if (i<n&&a[i+1])
			{
				int j=i+1;
				if (a[j]<a[j-1])
				{
					while (j<=n&&a[j]&&a[j]<a[j-1]) {ans+=2*calc(a[j],a[i],m);j++;}
				}
			}
		}
	for (int i=1;i<=m;i++)
		if (b[i])
		{
			ans+=2*calc(b[i],b[i],n);
			if (i>1&&b[i-1])
			{
				int j=i-1;
				if (b[j]<b[j+1])
				{
					while (j>0&&b[j]&&b[j]<b[j+1]) {ans+=2*calc(b[j],b[i],n);j--;}
				}
			}
			if (i<m&&b[i+1])
			{
				int j=i+1;
				if (b[j]<b[j-1])
				{
					while (j<=m&&b[j]&&b[j]<b[j-1]) {ans+=2*calc(b[j],b[i],n);j++;}
				}
			}
		}
//	cout<<ans<<endl;
	double _ans=double(ans)/double(tot)/double(tot);
	printf("%.4lfn",_ans);


//	while (1);

	return 0;


}

CF 237C (质数区间)

给定区间[a,b] 求l的最小值使[a,b]中任意长度为l的一段包含至少k个Prime

二分l

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iostream>
using namespace std;
#define MAXN (1000000+10)
int a[MAXN],tot=0,x,y,k;
bool b[MAXN]={0};
void work()
{
	b[1]=1;
	for (int i=2;i<=y;i++)
	{
		if (!b[i])
		{
			tot++;
			a[tot]=i;
		}
		for (int j=1;j<=tot;j++)
		{
			if (a[j]*i>y) break;
			b[a[j]*i]=1;
			if (!i%a[j]) break;
		}
	}
}
bool is_ok(int l)
{
	int tot=0;
	for (int i=x;i<=x+l-1;i++)
		if (!b[i]) tot++;
	if (tot<k) return false;
	for (int j=x+l;j<=y;j++)
	{
		tot=tot+(!b[j])-(!b[j-l]);
		if (tot<k) return false;
	}
	return true;
}

int main()
{
	scanf("%d%d%d",&x,&y,&k);
	work();
	int l=k,r=y-x+1;
	if (l>r||!is_ok(r))
	{
		printf("-1n");
		return 0;
	}
	for (int i=1;i<=60;i++)
	{
		if (l==r) break;
		int m=(l+r)>>1;
//		if (r-l==1) m++;
		if (is_ok(m)) r=m;
		else l=m+1;
	}
	printf("%dn",l);
//	while (1);
	return 0;
}

CF 237A (Cash)

题目大意:是一堆人来h点m分来超市买东西,同时可以有an位顾客买单,买单可认为1分钟以内完成,问至少有几位售货员才能使所有顾客不等待

直接统计……

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>

using namespace std;
#define MAXN (100000+10)
int n,i;
int a[MAXN]={0};
int main()
{
	int ans=0;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		int h,m;
		scanf("%d%d",&h,&m);
		a[h*60+m]++;
		ans=max(ans,a[h*60+m]);
	}
	printf("%dn",ans);

//	while (1);
	return 0;
}

舞蹈课 (C++堆的优先级与重载)

第三题:舞蹈课(dancingLessons)

时间限制:1秒

内存限制:256MB

输入:dancingLessons.in

输出:dancingLessons.out

问题描述

有n个人参加一个舞蹈课。每个人的舞蹈技术由整数来决定。在舞蹈课的开始,他们从左到右站成一排。当这一排中至少有一对相邻的异性时,舞蹈技术相差最小的那一对会出列并开始跳舞。如果相差最小的不止一对,那么最左边的那一对出列。一对异性出列之后,队伍中的空白按原顺序补上(即:若队伍为ABCD,那么BC出列之后队伍变为AD)。舞蹈技术相差最小即是的绝对值最小。

你的任务是,模拟以上过程,确定跳舞的配对及顺序。

输入

第一行为正整数:队伍中的人数。下一行包含n个字符B或者G,B代表男,G代表女。下一行为n个整数。所有信息按照从左到右的顺序给出。在50%的数据中,。

输出

第一行:出列的总对数k。接下来输出k行,每行是两个整数。按跳舞顺序输出,两个整数代表这一对舞伴的编号(按输入顺序从左往右1至n编号)。请先输出较小的整数,再输出较大的整数。

样例输入

4

BGBG

4 2 4 3

样例输出

2

3 4

1 2

样例输入

4

BGBB

1 1 2 3

样例输出

1

1 2

堆的操作

--堆默认为小根堆(重载Operator<) 小的放前面

注意此处用greater - 大根堆

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (200000 + 10)
#define MAXAI (10000000 + 10)
int n;
char s[MAXN];
int a[MAXN];
bool b[MAXN];
struct pair2
{
	int x,y,w;
}ans[MAXN];

bool operator>(const pair2 a,const pair2 b)
{
	if (a.w==b.w) return a.x>b.x;
	return a.w>b.w;
}
void cout_pair(const pair2 a)
{
	printf("%d %dn",a.x,a.y);
}
priority_queue <pair2, vector<pair2>, greater<pair2> > q;
void push(int x,int y)
{
	pair2 now;
	now.x=x;
	now.y=y;
	now.w=abs(a[x]-a[y]);
	q.push(now);
//	cout<<"add "<<now.x<<' '<<now.y<<' '<<now.w<<'n';
}
int main()
{
	freopen("dancingLessons.in","r",stdin);
	freopen("dancingLessons.out","w",stdout);


	scanf("%dn%s",&n,s);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	memset(b,0,sizeof(b));
	for (int i=1;i<n;i++)
		if (s[i-1]!=s[i]) push(i,i+1);
	int tot=0;
	while (!q.empty())
	{
		pair2 now=q.top();
//		cout_pair(now);
		q.pop();
		if (b[now.x]||b[now.y]) continue;
		b[now.x]=1;
		b[now.y]=1;
		ans[++tot]=now;
		int l=now.x-1,r=now.y+1;
		if (l<1||r>n) continue;
		while (l>1&&b[l]) l--;
		while (r<n&&b[r]) r++;
		if (b[l]||b[r]) continue;
		if (s[l-1]!=s[r-1]) push(l,r);


	}

	printf("%dn",tot);
	for (int i=1;i<=tot;i++) cout_pair(ans[i]);

//	while (1);
	return 0;
}

BYSBZ 2748(音量调节-01背包)

第一题:音量调节 (changingsounds)

时间限制:1秒

空间限制:64 MB

输入:changingsounds.in

输出:changingsounds.out

问题描述

一个吉他手准备参加一场演出。他不喜欢在演出时始终使用同一个音量,所以他决定每一首歌之前他都要改变一次音量。在演出开始之前,他已经做好了一个列表,里面写着在每首歌开始之前他想要改变的音量是多少。每一次改变音量,他可以选择调高也可以调低。

音量用一个整数描述。输入文件中给定整数beginLevel,代表吉他刚开始的音量,以及整数maxLevel,代表吉他的最大音量。音量不能小于0也不能大于maxLevel。输入文件中还给定了n个整数,表示在第i首歌开始之前吉他手想要改变的音量是多少。

吉他手想以最大的音量演奏最后一首歌,你的任务是找到这个最大音量是多少。

输入

第一行依次为三个整数:n, beginLevel, maxlevel。

第二行依次为n个整数:。

限制:,,,。

输出

输出演奏最后一首歌的最大音量。如果吉他手无法避免音量低于0或者高于maxLevel,输出-1。

样例输入

3 5 10

5 3 7

样例输出

10

样例输入

4 8 20

一定要读题啊!!

这题说的是——每次一定换,没说前后不能到一个音量。

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<functional>
#include<algorithm>
#include<stack>
using namespace std;
#define MAXN  (50 + 10)
#define MAXMAXLEVEL (1000+1)
int n,start,maxlevel,a[MAXN];
bool f[MAXN][MAXMAXLEVEL];
int main()
{
//  freopen("changingsounds.in","r",stdin);
//  freopen("changingsounds.out","w",stdout);

    memset(f,0,sizeof(f));

    scanf("%d%d%d",&n,&start,&maxlevel);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    f[0][start]=1;
    for (int i=1;i<=n;i++)
        for (int j=0;j<=maxlevel;j++)
        {
            f[i][j]=j-a[i]>=0? f[i-1][j-a[i]] : 0;
            f[i][j]=j+a[i]<=maxlevel? f[i-1][j+a[i]]||f[i][j] : f[i][j];



        }
    int ans=maxlevel;
    while (ans>=0&&!f[n][ans]) ans--;
    cout<<ans<<endl;




//  while (1);
    return 0;
}

POJ 3270(Cow Sorting)

这题主要是交换时要求代价最小

先找到环   相同数字 与   同列 相连

1  第一行为起始序列   第二行为目标序列

   1 4 2 5

   1 2
3 4 5

把一个环中最小的那个与指向的数交换

   1 3
2 4 5

   1 2
3 4 5

最后交换3 2

   1 2
3 4 5

   1 2
3 4 5

或则调来序列中最小的那个数与环中最小数替换(乘船问题?)

这样就能得到最优解

ans+=min(  (sizi-2)*mini+sumi, 

    (sizi+1)*minn+mini+sumi

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (10000+10)
#define MAXAI (100000+10)
int n,a[MAXN],a2[MAXN],hpos[MAXAI],ans=0;
bool b[MAXN];

int main()
{
//	freopen("cowsorting.in","r",stdin);
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	memcpy(a2,a,sizeof(a));
	sort(a2+1,a2+1+n,less<int>());
	int minn=a2[1];
	memset(b,false,sizeof(b));
	for (int i=1;i<=n;i++)
		hpos[a[i]]=i;


	for (int i=1;i<=n;i++)
	{
//		cout<<a2[i]<<' '<<a[i]<<endl;
		if (!b[i]&&a2[i]!=a[i])
		{
			b[i]=1;
			int mini=a[i],start=i,sizi=1,sumi=a[i];
			int now=hpos[a2[i]];
			while (now!=start)
			{
				b[now]=1;
				sizi++;
				sumi+=a[now];
				mini=min(mini,a[now]);
				now=hpos[a2[now]];
			}
			ans+=min((sizi-2)*mini+sumi,(sizi+1)*minn+mini+sumi);

		}
	}





///000
//	for (int i=1;i<=n;i++) printf("%d ",a2[i]);
///000
	printf("%dn",ans);

//	while (1);
	return 0;
}

NOIP's 算法

NOINOIP算法考前总结,带*NOIP级别的比赛中涉及到的几率不大的算法

一、基础算法

一般都是前几题,会比较简单

1、模拟   注意要写准确,注意细节,简单的模拟一定要AC,复杂的模拟要先用手写清楚再开始遍  例如noip2011
mayan

2、搜索(枚举)   dfs,bfs,同样简单的尽量AC。有些搜索需要剪枝,尽量刨去不会出现的答案

3、贪心   重点在证明,如果实在没有好的方法又不会证明可以用来骗骗分 例如noip2011
bus

4*、矩阵  关键是构造出正确的矩阵,实现很简单,范围大时要用快速幂

二、动态规划

一般每年必考至少一道

DP题时,最好先在纸上写好状态转移方程和边界条件

1、背包  分为01背包,部分背包,完全背包等。例如  采药,金明的预算方案等

2、线性DP  这例如 添加号,ioi2003方块消除

3、坐标型DP  例如  noi99棋盘分割

4、区间型DP  例如  noip2005能量项链,noip2008传球游戏

5、树形DP  例如 皇宫看守,noi2012
park
poi-6 tro,poj1947

6、*状压DP  本质就是用一个数字表示一个状态,一般有最小表示法和括号表示法。 例如noi2001炮兵阵地,noi2007生成树计数,SCOI2005互不侵犯的king

7、*斜率优化DP  这类题需要手推公式,然后找到单调性。 例如HNOI2008玩具装箱,APIO2010特别行动队

三、图论

       这类题种类非常多

1、最短路  必会的算法,spfa,dijkstradijkstra注意用堆优化,尽量用dij+heap而不是spfa 这类题到处都是

2、最小生成树  稀疏图用kruskal,稠密图用primprim也可以堆优化

3、LCA  会一种求LCA的方法就够了,比如Tarjan的离线算法,时间复杂度是O(n+Q) 例如BZOJ1776

4、强连通分量  Tarjan算法。 例如BZOJ1051

5、欧拉路的问题  例如 sgu101

6、二分图最大匹配 重点是建模。例如BZOJ1059BZOJ1433,BZOJ1562

7、*最小费用最大流  重点同样是建模。例如noi2012delicacysgu185

四、数据结构

       NOIP涉及的较少

1、   队列  都是基本算法,必须掌握

2、  并查集 例如 亲戚,noi食物链,BZOJ1015

3、  字典树 快速查找到字符串的方法,一般不单独出现,用来优化其它算法

4、  线段树 很常用的数据结构。例如 BZOJ1798      ,BZOJ1651BZOJ1230BZOJ1858

5、  *平衡树 用来维护二叉查找树的。 例如BZOJ1208BZOJ1503BZOJ1588

6、  *可并堆(斜堆、左偏树),有时启发式合并也不足以解决问题就要用它了。 例如APIO2012派遣

7、  *伸展树 用来维护序列的增加一个序列,删除一个序列,翻转一个序列等操作。 例如NOI2005序列维护,NOI2003editor

8、  *划分树 求区间第K 例如poj2104

9、  *AC自动机,多串匹配的经典算法。例如noi2011阿狸的打字机

10、            *二维线段树 类似一维线段树,只不过每个节点存的是一颗线段树,实际就是线段树套线段树。例如noi2012
chess

BYSBZ 1696(建牛舍)

中位数 贪心 

x,y 分开考虑

显然中间那个点一定最省 

1.

奇数 显然中间那个点一定最省  之后走一步答案必会增加,因此中间和4个方向一定最省,另外4个方向增加是对应2个方向的和

2

偶数

判定整个范围会超时

枚举牛而非整个区间,否则TLE


#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<functional>
#include<algorithm>
#define MAXN (10000 + 10)
#define INF 400000000 + 10
using namespace std;
int x[MAXN],y[MAXN];
int a[4][2]={{1,0},{-1,0},{0,-1},{0,1}};
int node[MAXN][2];
int n;
int sum(int nx,int ny)
{
	int tot=0;
	for (int i=1;i<=n;i++)
	{
		tot+=abs(x[i]-nx)+abs(y[i]-ny);
	}
	return tot;
}
int b(int x,int y)
{
	for (int i=1;i<=n;i++)
		if (node[i][0]==x&node[i][1]==y) return true;
	return false;
}
void find(int &minway,int &ans,int x,int y)
{
	int nowtot=sum(x,y);
	if (nowtot<minway)
	{
		ans=1;
		minway=nowtot;
	}
	else if (nowtot==minway) ans++;

}


int calc()
{
	int m=(1+n)/2;
	if (n%2)
	{
		if (!b(x[m],y[m]))
		{
			printf("%d",sum(x[m],y[m]));
			return 1;
		}
		else
		{
			int minway=INF,ans=0;
			for (int i=0;i<4;i++)
			{
				find(minway,ans,x[m]+a[i][0],y[m]+a[i][1]);
			}
			printf("%d",minway);
			return ans;
		}

	}
	else
	{
		int ans=0;
		for (int i=1;i<=n;i++)
		{
			if (x[m]<=node[i][0]&&node[i][0]<=x[m+1]&&y[m]<=node[i][1]&&node[i][1]<=y[m+1]) ans++;
		}
		ans=(x[m+1]-x[m]+1)*(y[m+1]-y[m]+1)-ans;
		printf("%d",sum(x[m],y[m]));
		return ans;
	}
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
		node[i][0]=x[i];
		node[i][1]=y[i];
	}
	sort(x+1,x+n+1);
	sort(y+1,y+n+1);
	printf(" %dn",calc());
	return 0;
}