连续背包 (背包套背包)

连续背包(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;
}

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

第二题:旅行(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;


}

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;
}

Number (dp-性质数状态表示)

Number

【题目描述】

明明在做力学作业的时候发现一类数非常有趣,他们和杠杆有比较相似的结构。这类数有这样的性质:

把某一位当成支点的话,那么左边的数字到这个点的力矩和等于右边的数字到这个点的力矩和,力矩可以理解为距离乘以数字。

举个例子,4139就是满足条件的数字,把3当成支点,我们有这样的等式4 * 2 + 1 *1 = 9 * 1。

小明想知道在一个区间[x,y]中,有多少个这样的数。

 

【输入数据】

两个数,表示x,y。 0 <= x,y<= 1018

 

【输出数据】

一个输出,表示区间[x,y]中满足条件的数的个数。

 

【样例输入】

7604 24324

 

【样例输出】

897

 

【数据范围】

0 <= x,y<= 1018


#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXS 810 + 10 +4000
#define MAXx 1000000000000000000
#define MAXI 30
long long f[MAXI][MAXS][2],depth,a[MAXI],ans,pos;
  //0 <= 只能取<=第i位的数,否则超过X越界    1 高位已经取了更小的 位数 后面随便取
long long dfs(int i,int s,int c)
{

	if (s<0||s>MAXS) return 0;
	if ((i==0)&&((s)||(c))) return 0;
	if (i==0) {//cout<<i<<' '<<s<<' '<<c<<' '<<f[i][s][c]<<endl;

	return 1;}
//	cout<<i<<' '<<s<<' '<<c<<' '<<f[i][s][c]<<endl;

	if (f[i][s][c]>=0) return f[i][s][c];
	else f[i][s][c]=0;
	if (!c)
	{
	//	cout<<"push "<<a[i]<<endl;
		f[i][s][c]+=dfs(i-1,s-(pos-i)*a[i],0);
	}
	else
	{
		for (int k=0;k<a[i];k++) //{ cout<<"push "<<k<<endl;
		f[i][s][c]+=dfs(i-1,s-(pos-i)*k,0); // cout<<"pop "<<k<<endl;}
		for (int k=0;k<=9;k++)//{  cout<<"push "<<k<<endl;
		f[i][s][c]+=dfs(i-1,s-(pos-i)*k,1); //cout<<"pop "<<k<<endl;}
	}

	return f[i][s][c];
}
long long calc(long long x)
{

	if (!x) return 0;  //<0的杠杆数为0
	long long ans=0;
	memset(f,0,sizeof(f));
	depth=0;
	while (x)
	{
		depth++;
		a[depth]=x%10;
		x=x/10;
	}
	for (int i=1;i<=depth/2;i++)
	{
		swap(a[i],a[depth-i+1]);
	}
	ans=0;
	f[depth+1][0][0]=1;   // =29333
	f[depth+1][0][1]=1;	  // <29333
	for (pos=1;pos<=depth;pos++)
	{
		memset(f,128,sizeof(f));
//		cout<<f[19][0][0]<<endl;
		ans+=/*dfs(depth,0,0)+*/dfs(depth,0,1);
		ans--;		//不考虑 0 它无论支点为几都出现
	}
	return ans+1;   //最后 加上 0
/*
	f[i][s][c]=f[i-1][s-(k-i)*j][0]

*/
}
int main()
{
	freopen("number.in","r",stdin);
	freopen("number.out","w",stdout);
	long long x,y;
	scanf("%lld%lld",&x,&y);
	printf("%lld",calc(y+1)-calc(x));
//	while (1);
}


不等数列 (Dp插入e)

【题目描述】

将1到n任意排列,然后在排列的每两个数之间根据他们的大小关系插入“>”和“<”。问在所有排列中,有多少个排列恰好有k个“<”。答案对2012取模。

 

【输入格式】

第一行2个整数n,k。

 

【输出格式】

一个整数表示答案。

 

【样例输入】

5 2

【样例输出】

66

【数据范围】

对于30%的数据:n <= 10

对于100%的数据:k < n <= 1000,



F[i,j]=f[i-1,j](j+1)+f[i-1][j-1](i-j)   i表示从1到i的数   j表示小于号个数

          插入最大数e后添了一个>

          或者一个<的数量


const
   maxn=1000;
var
   f:array[0..1000,0..1000] of longint;
   n,i,j,k:longint;
begin
   assign(input,'num.in');
   assign(output,'num.out');
   reset(input);
   rewrite(output);
   read(n,k);
   if (k>n-1-k) then k:=n-1-k;
   for i:=1 to n do
   begin
      f[i,0]:=1;
      f[i,i-1]:=1;
   end;
   for i:=2 to n do
      for j:=1 to k do
         f[i,j]:=(f[i-1,j]*(j+1)+f[i-1,j-1]*(i-j)) mod 2012;
   writeln(f[i,k]);
   close(input);
   close(output);
end.

另附 暴搜%30数据的版本

var
   b:array[1..10] of boolean;
   f:array[0..10] of longint;
   n,k,i:longint;
procedure dfs(l,x,father:longint);
var
   i:longint;
begin
   if l=n then begin inc(f[x]); exit; end;
   for i:=1 to n do
   if not(b[i]) then
   begin
      b[i]:=true;
      if father<i then dfs(l+1,x+1,i) else
      dfs(l+1,x,i);
      b[i]:=false;
   end;

end;
begin
   readln(n,k);
   fillchar(b,sizeof(b),false);
   fillchar(f,sizeof(f),0);
   for i:=1 to n do
   begin
      b[i]:=true;
      dfs(1,0,i);
      b[i]:=false;
   end;
   for i:=0 to n-1 do write(f[i],' ');


end.

RQNOJ 658(观光公交)

几大注意点:

1.一次使用氦气加速器会把后面分成好几段。

2.我们仅维护end[i],wait[i]恒定,因此需提前让wait[i]=max(wait[i-1],wait[i]);

3.w[i]+w[i+1]+...+w[j],且w恒定,故可预处理sum[i](满足累加性)

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<string>
#include<algorithm>
#include<queue>
#include<functional>
#include<iostream>
#define MAXN 1000 +10
#define MAXM 10000 + 10
#define MAXK 100000 + 10
#define MAXD 100 + 10
#define MAXT 100000 + 10
using namespace std;
struct man
{
	int t,l,r;
}t[MAXM];
struct interval
{
	int l,r,w;
};

int n,m,k,ans,d[MAXN+1],wait[MAXN+1],w[MAXN+1],end[MAXN+1],sum[MAXN+1];
bool operator<(const interval a,const interval b)   //重载  operator < 的意思   为q做准备
{


	return a.w<b.w;
}

priority_queue<interval> q;   //priority_queue<Node, vector<Node>, greater<Node> >; 完整的不能用


void push(int l,int r)
{
	bool flag=0;
	for (int i=l;i<r;i++) if (d[i]) {flag=1;break;}
	if (!flag) return;
	int tot=sum[r]-sum[l];
	if (!tot) return ;
	interval now;
	now.w=tot;
	now.l=l;
	now.r=r;
	q.push(now);

//	 cout <<"add "<< now.l << ' ' << now.r <<' '<< now.w << endl;
}

int main()
{
//	freopen("qc.in","r",stdin);
	memset(wait,0,sizeof(wait));
	memset(w,0,sizeof(w));

	scanf("%d%d%d",&n,&m,&k);
	for (int i=1;i<=n-1;i++)
		scanf("%d",&d[i]);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&t[i].t,&t[i].l,&t[i].r);
		wait[t[i].l]=max(wait[t[i].l],t[i].t);
		w[t[i].r]++;
	}
	end[1]=0;
	for (int i=2;i<=n;i++)
	   end[i]=max(end[i-1],wait[i-1])+d[i-1];
	ans=0;
	for (int i=1;i<=m;i++)
	   ans+=end[t[i].r]-t[i].t;

	sum[1]=sum[0]=0;
	for (int i=2;i<=n;i++) {sum[i]=sum[i-1]+w[i]; wait[i]=max(wait[i-1],wait[i]);}

//	for (int i=0;i<=n;i++) cout<<end[i]<<' ';
//	cout<<endl;


	int tot=d[1],head=1;
	for (int i=2;i<=n;i++)
	{
		if (wait[i]>=end[i])
		{
			if (tot) push(head,i);
			tot=0;
			head=i;
		}
		tot+=d[i];
		/*
		tot+=w[i]*min(1,d[i]);
//		tot+=w[i];
		*/
	}


	if (tot) push(head,n);


//00	cout<<ans<<endl;
	//贪心
//	sort(t+1,t+m+1,cmp);

// 调试
/*   while( !q.empty() ){
        cout << q.top().l << ' ' << q.top().r <<' '<< q.top().w << endl;
        q.pop();
	}
*/
/*
	for (int i=1;i<=n;i++)
	{
		cout<<end[i]<<' ';
	}
*/
	while (k&&!q.empty())
	{
/*	cout<<"/////////"<<endl;
	for (int i=1;i<=n;i++)
	{
		cout<<end[i]<<' ';
	}
	cout<<endl;
	for (int i=1;i<=n;i++)
	{
		cout<<wait[i]<<' ';
	}

	cout<<endl;
*/
		interval now=q.top();
		q.pop();
		ans-=now.w;
		int i=now.l;
//000
//     cout << now.l << ' ' << now.r <<' '<< now.w << endl;
//      cout << ans << endl;
//000


		while (!d[i]) i++;
		d[i]--;
		int head=i,tot=0,i2=0;
		i++;
		while (i<=now.r/*&&wait[i]<end[i]*/)
		{
		//	tot+=w[i-1];
			end[i]--;
			//000
//		    for (int cse=1;cse<=n;cse++) cout<<end[cse]<<',';
//			cout<<endl;
			//-
//			cout<<i<<endl;
//			cout<<wait[i+1]<<' '<<end[i+1]<<endl;
			if (wait[i]==end[i])
			    {push(head,i);head=i;}
			i++;
		}

		i--;
		if (head!=now.r) push(head,now.r);

/*
		if (i>now.r) {cout<<"smile"<<endl;
			cout<<'<'<<' '<<i<<' '<<wait[i]<<' '<<end[i]<<' '<<endl;}
*/
//		cout<<"i2:"<<i2<<endl;
/*		if (i2) i=i2;
		i--;
		push(head,i);
		if (i<now.r)
		{
//			if (!d[head]) tot+=w[head];
			push(i,now.r);
		}
*/

		//处理 end[i] 显然最多影响到 now.r
		k--;
	}

	cout<<ans<<'n';


// 调试
/*
	    while( !q.empty() ){
        cout << q.top().l << ' ' << q.top().r <<' '<< q.top().w << endl;
        q.pop();
	}
	for (int i=1;i<=n;i++)
	{
		cout<<end[i]<<' ';
	}



*/


//	while (1);

}

POJ 3661(贝茜晨练)

经典Dp,果断记忆化……

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<functional>
using namespace std;
#define MAXN 10000 + 10
#define MAXM 500 + 10
int f[MAXN][MAXM],n,m,d[MAXN];
int dfs(int i,int j)
{
	if (i==0) return 0;
	if (f[i][j]) return f[i][j];
	if (i&&!j) f[i][j]=dfs(i-1,0);
	if (j)
		 f[i][j]=dfs(i-1,j-1)+d[i];
	else
	{
		for (int k=min(i,m);k>0;k--)
		{
	          f[i][j]=max(f[i][j],dfs(i-k,k));
		}
	}
	return f[i][j];
}
int main()
{
//	freopen("running.in","r",stdin);
	memset(f,0,sizeof(f));
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%d",&d[i]);

	printf("%dn",dfs(n,0));

//	while (1);
	return 0;
}

RQNOJ 38(串的计数)

一个长度为3N字符串满足:由N个A,N个B,N个C组成,对于它的任意前缀,满足A的个数>=B的个数>=C的个数。求满足这样条件的字符串的个数。


看到这题第一反应:打表!第二反应三个字:高精度

这题动态转移方程为f[i,j,k]=f[i-1,j,k]+f[i,j-1,k]+f[i,j,k-1]  i,j,k表示A,B,C的个数,考察是否可行

高精啊高精……

Program str;
type
   arr=record
           len:longint;
           d:array[1..15] of longint;
       end;
var
   n,i,j,k:longint;
   f:array[0..60,0..60,0..60] of arr;
function max(a,b:longint):longint;
begin
   if a>b then exit(a) else exit(b);
end;
procedure sum(a,b:arr;var c:arr);
var
   i,j:longint;
const
   F=100000000;

begin
   fillchar(c,sizeof(c),0);
   c.len:=max(a.len,b.len);
   for i:=1 to c.len do
   begin
      inc(c.d[i],a.d[i]+b.d[i]);
      inc(c.d[i+1],c.d[i] div F);
      c.d[i]:=c.d[i] mod F;
   end;
   if c.d[c.len+1]>0 then inc(c.len);

   while (c.len>0) and (c.d[c.len]=0) do dec(c.len);


end;
Procedure print(a:arr);
var
   i,p:longint;
begin
   write(a.d[a.len]);
   for i:=a.len-1 downto 1 do
   begin
      p:=a.d[i];
      if p<10000000 then write('0');
      if p<1000000 then write('0');
      if p<100000 then write('0');
      if p<10000 then write('0');
      if p<1000 then write('0');
      if p<100 then write('0');
      if p<10 then write('0');
      write(p);
   end;
   writeln;
end;
begin
   read(n);
   fillchar(f,sizeof(f),0);
   f[0,0,0].len:=1;
   f[0,0,0].d[1]:=1;
   for i:=1 to n do
      for j:=0 to i do
         for k:=0 to j do
         begin
            if k>0 then
            begin
               sum(f[i,j,k],f[i,j,k-1],f[i,j,k]);
            end;
            if i>j then sum(f[i,j,k],f[i-1,j,k],f[i,j,k]);
            if j>k then sum(f[i,j,k],f[i,j-1,k],f[i,j,k]);
         end;

   print(f[n,n,n]);
end.

POJ 2184(01背包+滚动数组)

01背包 模板题

Program dd;
const
   maxn=1000;
   maxv=100000;
   minv=-100000;
   NULL=-2139062144;
var
   n,i,j,ans,p,np:longint;
   ts,tf:array[1..maxn] of longint;
   f:array[0..1,minv..maxv] of longint;
function max(a,b:longint):longint;
begin
   if a<b then exit(b) else exit(a);
end;
begin
   read(n);
   for i:=1 to n do read(ts[i],tf[i]);
   fillchar(f,sizeof(f),128);
   f[0,0]:=0;
   for i:=1 to n do
   begin
      p:=i mod 2;
      fillchar(f[p],sizeof(f[p]),128);
      np:=(p+1) mod 2;

      for j:=maxv downto minv do
      begin
         f[p,j]:=f[np,j];
         if (minv<=j-ts[i]) and (j-ts[i]<=maxv) then
            if (f[np,j-ts[i]]<>NULL) then
               f[p,j]:=max(f[p,j],f[np,j-ts[i]]+tf[i]);



      end;
   end;


   ans:=0;
   for i:=0 to maxv do
      if (f[n mod 2,i]>=0) and (ans<f[n mod 2,i]+i) then ans:=f[n mod 2,i]+i;

   writeln(ans);

end.

POJ 1276(多重背包)

RT

count 表示 第i种面额在f[j] 放的数量

Program P1276;
const
   maxn=20;
   maxcash=1000000;
var
   cash,n,i,j:longint;
   cost,d:array[1..maxn] of longint;
   f,count:array[0..maxcash] of longint;
begin
   while not seekeof do
   begin
      read(cash,n);
      for i:=1 to n do read(d[i],cost[i]);
      fillchar(f,sizeof(f),0);
      for i:=1 to n do
      begin
         fillchar(count,sizeof(count),0);
         for j:=cost[i] to cash do
         begin
            if (f[j]<f[j-cost[i]]+cost[i]) and (count[j-cost[i]]<d[i]) then
            begin
               f[j]:=f[j-cost[i]]+cost[i];
               count[j]:=count[j-cost[i]]+1;
            end;
         end;
      end;

      writeln(f[cash]);
   end;
end.