POJ 1631(O(nlogn)LIS的2种做法)

Language:
Bridging signals
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 8574   Accepted: 4635

Description

对于一个二分图的完全匹配,请找出最多的边使其两两不相交。

Input

第一行为测试数据数t,
对于每组数据,第一行为匹配数 p < 40000,
接下来p行,每行1个数a[i],表示左边第i个端点与右边第a[i]个端点相连

Output

对每组数据,输出一行ans,表示最大不相交匹配数

Sample Input

4
6
4
2
6
3
1
5
10
2
3
4
5
6
7
8
9
10
1
8
8
7
6
5
4
3
2
1
9
5
8
9
2
3
1
7
4
6

Sample Output

3
9
1
4

Source

这题显然可以转化为a[i]的LIS

LIS的一般做法如下:

f[i]表示以i为最后一个元素的最长序列数,

f[i]=f[j]+1(a[j]<a[i],j<i)

nLogn 算法1:

显然上面的方程有1维n是用来求‘小于a[i]且在a[i]前面的,最大的数‘

单从这个定义考虑,

于是问题转化成-维护序列max(f[i]),每一次增加1个点的值,求[1,value_i)的最大值(若无值则为0)

不妨用一个zkw线段树维护(本题max(f[i])的长度=n,若没有这个条件时间会退化到O(nLogT)(T为a[i]的最大值),那么请把原序列排序O(nLogn)+离散化O(n),这样复杂度就有O(nLogT)降至O(nLogn)    ).

程序1如下:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (40000+10)
#define NDEBUG
int t,n;
struct SegMentTree
{
	int a[MAXN*10],n;
	int M;
	SegMentTree(){}
	SegMentTree(int _n):n(_n)
	{
		M=1;
		while (M-2<n) M<<=1;
		memset(a,0,sizeof(a));
	}
	void insert(int _x,int c)
	{
		_x+=M;
		if (a[_x]<c)
		{
			a[_x]=c;
			for (_x>>=1;_x;_x>>=1) a[_x]=max(a[_x<<1],a[(_x<<1)^1]);
		}
	}
	int find(int l,int r)
	{
		int ans=0;
		l--;r++;
		l+=M;r+=M;
		while (l^r^1)
		{
			if (~l&1) ans=max(ans,a[l+1]);
			if (r&1) ans=max(ans,a[r-1]);
			l>>=1;r>>=1;
		}
		return ans;
	}
}a;
int main()
{
	#ifndef NDEBUG
	freopen("poj1631.in","r",stdin);
	#endif
	scanf("%d",&t);
	while (t--)
	{
		cin>>n;
		a=SegMentTree(n);
		for (int i=1;i<=n;i++)
		{
			int value;
			scanf("%d",&value);
			a.insert(value,a.find(1,value-1)+1);
		}
		printf("%dn",a.find(1,n));

	}
	return 0;
}

算法2:

仔细观察推导序列求最大值部分,发现i总从1开始[1,value_i)

于是可二分查找序列Max[I]'=Max[ F[p] ] (1≤p≤i)

Program LCS;
var
   a,d,f:array[1..100000] of longint;
   n,i,j,len,test:longint;
function search(k:longint):longint;
var
   i,j,m:longint;
begin
   i:=1; j:=len;
   m:=d[(i+j) div 2];
   while (i<=j) do
   begin
      m:=(i+j) div 2;
      if (d[m]<k) and (d[m+1]>=k) then exit(m)
      else if (d[m]<k) then i:=m+1
      else j:=m-1;
   end;
end;
begin
   read(test);
   while (test>0) do
   begin
      read(n);
      len:=1;
      fillchar(d,sizeof(d),0);
      for i:=1 to n do read(a[i]);
      d[1]:=a[1];
      f[1]:=1;
      for i:=2 to n do
      begin
         if (a[i]>d[len]) then
         begin
            inc(len);
            d[len]:=a[i];
            f[i]:=len;
         end
         else if (a[i]<=d[1]) then
         begin
            d[1]:=a[i];
            f[i]:=1;
         end
         else
         begin
            j:=search(a[i]);
            d[j+1]:=a[i];
            f[i]:=j+1;
         end;
      end;
      writeln(len);
      dec(test);
   end;
end.




Tyvj P1180(情况少分支多的过程-Dp)

P1180 - 矿工配餐

From Admin    Normal (OI)
总时限:16s    内存限制:128MB    代码长度限制:64KB

描述 Description

现有两个煤矿,每个煤矿都雇用一组矿工。采煤工作很辛苦,所以矿工们需要良好饮食。每当一辆食品车到达煤矿时,矿工们便会产出一定数量的煤。有三种类型的食品车:肉车,鱼车和面包车。
矿工们喜欢变化的食谱。如果提供的食品能够不断变化,他们的产煤量将会增加。每当一个新的食品车到达煤矿时,矿工们就会比较这种新的食品和前两次(或者少于两次,如果前面运送食品的次数不足两次)的食品,并且:
如果这几次食品车都是同一类型的食品,则矿工们产出一个单位的煤。
如果这几次食品车中有两种不同类型的食品,则矿工们产出两个单位的煤。
如果这几次食品车中有三种不同类型的食品,则矿工们产出三个单位的煤。
预先已知食品车的类型及其被配送的顺序。通过确定哪车食品送到哪个煤矿可以影响产煤量。食品车不能被拆分,每个食品车必须被全部送到一个或另一个煤矿。两个煤矿也并不要求接收相同数量的食品车(事实上,也允许将所有食品车都送到一个煤矿)。
任务
给出食品车的类型及其被配送的顺序,要求你写一个程序,确定哪个食品车应被送到煤矿1,哪个食品车应被送到煤矿2,以使得两个煤矿的产煤量的总和最大。

输入格式 InputFormat

输入的第一行包含一个整数N (1 ≤ N ≤ 100 000),  表示食品车的数目。
第二行包含一个由N个字符组成的字符串,按照配送顺序依次表示食品车配送的食品的类型。每个字符是以下三个大写字母之一:'M' (表示肉类), 'F' (表示鱼类) 或 'B' (表示面包)。

输出格式 OutputFormat

输出一个整数,表示最大的总产煤量。

样例输入 SampleInput [复制数据]

样例输入1
6
MBMFFB

样例输入2
16
MMBMBBBBMMMMMBMB

样例输出 SampleOutput [复制数据]

样例输出1
12

样例输入2
29

数据范围和注释 Hint

在样例1中,可以按照如下的顺序运送食品车:煤矿 1, 煤矿 1, 煤矿 2, 煤矿 2, 煤矿 1, 煤矿 2, 依次产生的产煤量为1, 2, 1, 2, 3 和 3 个单位,一共是12 个单位。还有其它运送方式也能产生上述最大总和的产煤量。

时间限制 TimeLimitation

前10点时限1s,分值8分
后2点时限3s,分值10分

这题看着就是不可解的。

但是仔细观察会发现它的情况较少,分支巨多(2^N)??

于是我们用滚动数组+Dp显然可以用F[i][j][k][l][m]表示子结构

j,k,l,m为2个矿工最近2次的伙食(0表示没有)

任何坑爹的题目先想想能不能Dp……

记忆化搜索不能滚动还会爆栈,于是Dp的存在性证毕。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (100000+10)
#define NDEBUG
int n,a[MAXN];
int f[2][4][4][4][4]; //1->M 2->F 3->B
int cost[4][4][4]={0};
int _cost(int i,int j,int k)
{
	if (k==0) return 0;
	if (i==0) i=k;
	if (j==0) j=k;
	if (i!=j&&i!=k&&j!=k) return 3;
	if (i!=j||i!=k||j!=k) return 2;
	return 1;
}
int main()
{
	#ifndef NDEBUG
	freopen("Vijos1386.in","r",stdin);
	#endif
	scanf("%d",&n);getchar();
	for (int i=0;i<n;i++)
	{
		switch (getchar())
		{
		case 'M':a[i]=1;break;
		case 'F':a[i]=2;break;
		case 'B':a[i]=3;break;
		}
	}
	for (int i=0;i<=3;i++)
		for (int j=0;j<=3;j++)
			for (int k=0;k<=3;k++)
			{
				cost[i][j][k]=_cost(i,j,k);
				#ifndef NDEBUG
				cout<<i<<' '<<j<<' '<<k<<':'<<cost[i][j][k]<<endl;
				#endif
			}


	memset(f,128,sizeof(f));
	f[0][0][0][0][0]=0;
	for (int ii=0;ii<n;ii++)
	{
		int i=ii%2;
		for (int j=0;j<=3;j++)
			for (int k=0;k<=3;k++)
				for (int l=0;l<=3;l++)
					for (int m=0;m<=3;m++)
					{
						if (f[i][j][k][l][m]>=0)
						{
							f[i^1][k][a[ii]][l][m]=max(f[i^1][k][a[ii]][l][m],f[i][j][k][l][m]+cost[j][k][a[ii]]);
							f[i^1][j][k][m][a[ii]]=max(f[i^1][j][k][m][a[ii]],f[i][j][k][l][m]+cost[l][m][a[ii]]);
						}
					}
	}
	int ii=n%2,ans=0;
	for (int j=0;j<=3;j++)
		for (int k=0;k<=3;k++)
			for (int l=0;l<=3;l++)
				for (int m=0;m<=3;m++)
					ans=max(ans,f[ii][j][k][l][m]);
	cout<<ans<<endl;


	return 0;
}


RQNOJ 599(等价替代法)

查看题目 Show Problem

题目:[NOIP2010]乌龟棋

问题编号:599


题目描述

小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
乌龟棋的棋盘是一行N 个格子,每个格子上一个分数(非负整数)。棋盘第1 格是唯一的起点,第N 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。

乌龟棋中M 张爬行卡片,分成4 种不同的类型(M 张卡片中不一定包含所有4 种类型的卡片,见样例),每种类型的卡片上分别标有1、2、3、4 四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到
该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?

【数据范围】
对于30%的数据有1 ≤ N≤ 30,1 ≤M≤ 12。
对于50%的数据有1 ≤ N≤ 120,1 ≤M≤ 50,且4 种爬行卡片,每种卡片的张数不会超过20。
对于100%的数据有1 ≤ N≤ 350,1 ≤M≤ 120,且4 种爬行卡片,每种卡片的张数不会超过40;0 ≤ ai ≤ 100,1 ≤ i ≤ N;1 ≤ bi ≤ 4,1 ≤ i ≤M。输入数据保证N−;;1=Σb_i (1<=i<=M)

输入格式

输入文件的每行中两个数之间用一个空格隔开。
第1 行2 个正整数N 和M,分别表示棋盘格子数和爬行卡片数。
第2 行N 个非负整数,a1, a2, ……, aN,其中ai 表示棋盘第i 个格子上的分数。
第3 行M 个整数,b1,b2, ……, bM,表示M 张爬行卡片上的数字。
输入数据保证到达终点时刚好用光M 张爬行卡片,即N−;;1=Σb_i (1<=i<=M)

输出格式

输出只有1 行,1 个整数,表示小明最多能得到的分数。

样例输入

【输入输出样例1】
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1

【输入输出样例2】
13 8
4 96 10 64 55 13 94 53 5 24 89 8 30
1 1 1 1 1 2 4 1

样例输出

【输入输出样例1】
73

【输入输出样例2】
455

注意到本题每一个m<=40

且n,m1,m2,m3,m4只要能确定4个,另1个就能推出来

故可构造状态f[m1,m2,m3,m4] 

O(40^4) 稳过

Program chess;
const
   maxn=350;
   maxm=120;
   maxmi=40;
var
   n,m,i,j,k,l,p,cost:longint;
   a:array[1..maxn] of longint;
   f:array[0..maxmi,0..maxmi,0..maxmi,0..maxmi] of longint;
   totcard:array[1..4] of longint;
function max(a,b:longint):longint;
begin
   if a<b then exit(b) else exit(a);
end;
begin
   readln(n,m);
   for i:=1 to n do read(a[i]);
   fillchar(totcard,sizeof(totcard),0);
   for i:=1 to m do
   begin
      read(p);
      inc(totcard[p]);
   end;

   fillchar(f,sizeof(f),0);
   f[0,0,0,0]:=a[1];

   for i:=0 to totcard[1] do
      for j:=0 to totcard[2] do
         for k:=0 to totcard[3] do
            for l:=0 to totcard[4] do
               if (i+j+k+l>0) then
               begin
                  cost:=a[1+i*1+j*2+k*3+l*4];
                  if i>0 then f[i,j,k,l]:=max(f[i,j,k,l],f[i-1,j,k,l]);
                  if j>0 then f[i,j,k,l]:=max(f[i,j,k,l],f[i,j-1,k,l]);
                  if k>0 then f[i,j,k,l]:=max(f[i,j,k,l],f[i,j,k-1,l]);
                  if l>0 then f[i,j,k,l]:=max(f[i,j,k,l],f[i,j,k,l-1]);
                  inc(f[i,j,k,l],cost);
               end;
   writeln(f[totcard[1],totcard[2],totcard[3],totcard[4]]);




end.

Tyvj Q1028(调整法)

Q1028 - Unit7 - 堆积木

From Admin    Normal (OI)
总时限:10s    内存限制:128MB    代码长度限制:64KB

描述 Description

现在有N块积木,每块积木都有自重Weight和正常状态下的承重能力Force,现在要把这N块积木垂直的垒在一起,但是有可能某块积木的负重超过了它在正常状态下的承重能力,那么这块积木就有被压坏的危险,请问应该如何堆这N块积木使得N块积木中最大的压力指数最小。

这里定义压力指数为该积木的负重与其在正常状态下的承重能力的差值。

输入格式 InputFormat

第一行为一个正整数N,表示有N块积木。
第二行到第 N+1 行,每行两个整数数,分别是第i个积木的Weight和Force

输出格式 OutputFormat

输出共一行,表示最大压力指数的最小值。

样例输入 SampleInput [复制数据]

2
100 0
1000 100

样例输出 SampleOutput [复制数据]

0

数据范围和注释 Hint

样例解释:
把Weight为100的积木放在Weight为1000的积木上,下面积木的压力指数为100 - 100 = 0,另外一块积木的压力指数和它的相等。

对于30% 的数据,1 <= N <= 3
对于60% 的数据,1 <= N <= 1000
对于100%的数据,1 <= N <= 50000
对于100%的数据,1 <= Weight <= 10000,1 <= Force <= 10^9

时间限制 TimeLimitation

1s

来源 Source

tjz


对于最优方案:

因为F(N)-(w1+w2+..wn-1)<F(1)-(w2+...+wn-1+wn)

化简得  F(n)+wn<F1+w1

调整ing。。。

也就是说对于最优方案有 Fi+wi<Fi-1+wi-1


Program block;
uses math;
const
   maxn=50000;
var
   n,i,j,ans,totw:longint;
   w,f:array[1..maxn] of longint;

procedure qsort(l,r:longint);
var
   i,j,m,p:longint;
begin
   i:=l;
   j:=r;
   m:=w[(i+j) div 2]+f[(i+j) div 2];
   repeat
      while (w[i]+f[i]<m) do inc(i);
      while (w[j]+f[j]>m) do dec(j);
      if i<=j then
      begin
         p:=w[i];w[i]:=w[j];w[j]:=p;
         p:=f[i];f[i]:=f[j];f[j]:=p;
         inc(i);dec(j);
      end;
   until i>j;
   if (l<j) then qsort(l,j);
   if (i<r) then qsort(i,r);
end;
begin
   read(n);
   for i:=1 to n do
   begin
      read(w[i],f[i]);
   end;
   qsort(1,n);
   ans:=-f[1];
   totw:=0;
   for i:=1 to n do
   begin
      ans:=max(ans,totw-f[i]);
      inc(totw,w[i]);

   end;
   writeln(ans);

end.



行车(a1*b1+a1*b2+..a1*bn+a2*b1+…an*bn=(a1+..an)(b1+..bn) )

行车
(bicycle.pas/cpp)
题目描述
骑在自行车上,让微风追逐着他衣角,在不经意间捕获着一颗颗芳心,骄阳似乎也没有此时的他耀眼,这便是机房的骄傲——建德!
这是每天都会发生在附中门口的一幕。而为了每天能够领略不同的风景,捕获更多的芳心,建德打算制定n 条线路。为了简化起见,我们把这个世界想象成一个平面直角坐标系,而建德所在的福建师大附中则为原点。由于建德不能绕的太进,他每次路线的目的地都被限制在一个对应的右上角为(x, y),左下角为(-x,-y)的矩形内。
每次建德都会从原点直接沿直线走到目的地。显然,他走过了一个向量,这被数学控的“毛毡”称为这次的路线向量。他为了更好地规划线路,为每条线路定义了一个无聊值,即这次的路线向量和其余所有乊前的线路的向量的点积和【对于向量(x1,y1),(x2,y2),他们的点积和即为x1*x2+y1*y2】。建德希望合理的选择目的地,使得所有线路的无聊值乊和最小。
输入格式
第一行一个正整数n ,表示建德打算制定n 条旅行线路。
接下来 n 行,每行两个整数x , y ,描述一个限制目的地的矩形。
输出格式
一行一个整数,即最小的无聊值,保留 2 位小数。
样例输入
2
1 2
2 1
样例输出
-4.00
数据范围与约定
对于10% 的数据,保证0<n<=5,0<x,y<=5。
对于 30% 的数据,保证0<n<=20,0<x,y<=100。

对于 100% 的数据,保证0<n<=200,0<x,y<=200。

首先 根据公式  n个数和m个数两两相乘的结果为n个数的和与m个数的和的积

而且x和y互不影响

于是套公式-两两相乘/2-交集   结果为 f(n)=  (x1+..+xn)^2+x1^2+..xn^2  )/2

易证  f(n)=(x1+...+xn-1)* xn   + f(n-1)

所以  xn 要么最大,要么最小 才能使 f(n)有最大/最小值

因为不知道 (x1+..x(n-1)的正负 所以只能靠猜( 既考虑最大,也考虑最小)

回到原公式 显然  要是 f(n)有最小值 就要另 (x1+..+xn)有最小值

于是就是分组背包各种做……

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<cmath>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (200+10)
#define MAXV ((40000+100)*2)
#define MAXX (200+10)
#define f(i,j)  f[ (i) ][ (j)+20000   ]
int n;
long long ans=0;
bool f[MAXN][MAXV];
int x[MAXN];
int y[MAXN];
int sqr(int x)
{
	return x*x;
}
int main()
{
	freopen("bicycle.in","r",stdin);
	freopen("bicycle.out","w",stdout);
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		cin>>x[i]>>y[i];
		ans-=(long long)(sqr(x[i]))+sqr(y[i]);
	}
	memset(f,0,sizeof(f));
	f(0,0)=1;

	for (int i=1;i<=n;i++)
		for (int j=-i*200;j<=i*200;j++)
			f(i,j)=f(i-1,j-x[i])||f(i-1,j+x[i]);

	int j=0;

	while (!f(n,j)&&!f(n,-j)) j++;
	ans+=(long long)j*j;

//	cout<<j<<endl;

/*	int j=0;
	for (int i=0;i<=n;i++)
	{
		for (int j=-10;j<=10;j++)
			cout<<f(i,j);
		cout<<endl;
	}
*/
	memset(f,0,sizeof(f));
	f(0,0)=1;

	for (int i=1;i<=n;i++)
		for (int j=-i*200;j<=i*200;j++)
			f(i,j)=f(i-1,j-y[i])||f(i-1,j+y[i]);

	j=0;
	while (!f(n,j)&&!f(n,-j)) j++;

	ans+=(long long)j*j;

	cout.setf(ios::fixed);
	cout.precision(2);
	cout<<double(ans)/2<<endl;


//	while (1);



	return 0;
}

Tyvj Q1024(double的使用)

在C++中 double会以牺牲最小数为代价换取高位

另外请看好题目是求整数部分还是四舍五入

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
#include<string>
using namespace std;
#define MAXN (1000+10)
int a[MAXN][MAXN],n;
int main()
{
	long double ans=0.0;
	scanf("%d",&n);
	long long all=0;
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
			all+=((long long)n-i+1)*((long long)n-j+1);
	}

	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
	{
		scanf("%d",&a[i][j]);
		ans+=(long double)(a[i][j])*(long double)(n-i+1)*(long double)(n-j+1)*(long double)(i*j);

	//	cout<<ans<<' ';
	}

	cout.setf(ios::fixed);
	cout.precision(0);
	cout<<trunc(ans/(long double)(all))<<endl;



	return 0;
}

比赛 (long double 与fixed)

比赛

 (mat.pas/c/cpp)

【问题描述】

    有两个队伍A和B,每个队伍都有n个人。这两支队伍之间进行n场1对1比赛,每一场都是由A中的一个选手与B中的一个选手对抗。同一个人不会参加多场比赛,每个人的对手都是随机而等概率的。例如A队有A1和A2两个人,B队有B1和B2两个人,那么(A1 vs B1,A2 vs B2)和(A1 vs B2,A2 vsB1)的概率都是均等的50%。

    每个选手都有一个非负的实力值。如果实力值为X和Y的选手对抗,那么实力值较强的选手所在的队伍将会获得(X-Y)^2的得分。

    求A的得分减B的得分的期望值。

【输入格式】

    第一行一个数n表示两队的人数为n。

    第二行n个数,第i个数A[i]表示队伍A的第i个人的实力值。

    第三行n个数,第i个数B[i]表示队伍B的第i个人的实力值。

【输出格式】

    输出仅包含一个实数表示A期望赢B多少分。答案保留到小数点后一位(注意精度)

【样例输入】

    2

    37

    15

【样例输出】

    20.0

【数据规模】

    对于30%的数据,n≤50。

    对于100%的.据,n≤50000;A[i],B[i]≤50000。

 

∑(a[i]-b[i))^2

注意后缀数要在排序后T_Y

另外C++要用long double ---但是不能用.lf占位符

要用如下的格式

cout.setf(ios::fixed);
	cout.precision(1);



	cout<<ans/n<<endl;

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
#include<iostream>
using namespace std;
#define MAXN (50000+10)
long long n,a[MAXN],b[MAXN],sumb[MAXN],sumb2[MAXN];
int main()
{
    freopen("mat.in","r",stdin);
    freopen("mat.out","w",stdout);

    scanf("%d",&n);
    sumb[0]=sumb2[0]=0;
    for (int i=1;i<=n;i++)
    {
        cin>>a[i];
	}
    sort(a+1,a+1+n);
	for (int i=1;i<=n;i++)
    {
        cin>>b[i];
}
    sort(b+1,b+1+n);

	for (int i=1;i<=n;i++)
	{
		sumb[i]=sumb[i-1]+b[i];
        sumb2[i]=sumb2[i-1]+b[i]*b[i];
	}


	long double ans=0.0;


	for (int i=1;i<=n;i++) cout<<sumb[i]<<' ';
	cout<<endl;
	int r=0;
    for (int i=1;i<=n;i++)
    {
        while (r<n&&a[i]>b[r+1]) r++;


        ans+=a[i]*a[i]*(2*r-n);
        ans+=2*sumb2[r]-sumb2[n];

        ans-=2*a[i]*(2*sumb[r]-sumb[n]);

        /*
		long double tmp=0.0;
        //cout<<r<<endl;

		tmp+=r*a[i]*a[i]-2*a[i]*sumb[r]+sumb2[r];
        cout<<tmp<<endl;

		tmp-=(n-r)*a[i]*a[i]-2*a[i]*(sumb[n]-sumb[r])+sumb2[n]-sumb2[r];
        tmp/=n;

        ans+=tmp;


//		cout<<ans;
	*/
    }

//	cout<<ans<<endl;

	cout.setf(ios::fixed);
	cout.precision(1);



	cout<<ans/n<<endl;



//  while (1);
    return 0;
}

Tyvj Q1043(跳格游戏)

Problem C:
   简单DP  f[i][0]表示第偶数次到i,f[i][1]表示第奇数次到i
   f[i][0]=max(f[j][1])-a[i]  1<=j<i
   f[i][1]=max(f[j][0])+a[i]  1<=j<i

考虑不走这格的情况f[i][0]=f[i-1][0]  假设能这么走,显然不影响答案而少枚一维

由于偶数扣分 所以最后走的一定是单步

答案为f[n][1]

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<cmath>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (30000+10)
int n,a[MAXN],f[MAXN][2]={0};
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		f[i][1]=a[i];
	}
	for (int i=1;i<=n;i++)
	{
		f[i][1]=max(max(f[i][1],f[i-1][1]),f[i-1][0]+a[i] );

		f[i][0]=max(max(f[i][0],f[i-1][0]),f[i-1][1]-a[i]);
	}
/*	for (int i=1;i<=n;i++) cout<<f[i][1]<<' ';
	cout<<endl;
	for (int i=1;i<=n;i++) cout<<f[i][0]<<' ';
	cout<<endl;

*/
/*
	int ans=f[1][1];
	for (int i=2;i<=n;i++) ans=max(ans,max(f[i][1],f[i][0]));
	cout<<ans<<endl;
*/
	cout<<f[n][1]<<endl;

	return 0;
}

草草草 (f(x)背包)

第三题  草草草(grass)

问题描述:

    有N棵小草,编号0至N-1。乐滋滋巨神不喜欢小草,所以乐滋滋要剪草,使得N棵小草的高度总和不超过H。在第0时刻,第i棵小草的高度是h[i],接下来的每个整数时刻,会依次发生如下三个步骤:

(1)每棵小草都长高了,第i棵小草长高的高度是grow[i]。

(2)乐滋滋选择其中一棵小草并把它剪平,这棵小草高度变为0。注意:这棵小草并没有死掉,它下一秒还会生长的。

(3)乐滋滋计算一下这N棵小草的高度总和,如果不超过H,则完成任务,一切结束,    否则轮到下一时刻。

你的任务是计算:最早是第几时刻,乐滋滋能完成他的任务?如果第0时刻就可以完成就输出0,如果永远不可能完成,输出-1,否则输出一个最早的完成时刻。

输入格式:

第一行,两个整数N和H。 1 ≤ N ≤ 50,0 ≤ H ≤ 1000000。

第二行,N个整数,表示h[i]。0 ≤ h[i] ≤ 100000。

第三行,N个整数,表示grow[i]。1 ≤ grow[i] ≤ 100000。

    对于20%的数据, 1 N
7。

输出格式:

  一个整数,最早完成时刻或-1。

输入输出样例:

输入样例

3  16

5  8  58

2  1  1 

2  58

5  8

2  1

 

2  0

5  8

2  1

 

7  33

5  1  6  5  8  4  7

2  1  1  1  4  3  2

输出样例

1

0

-1

5

样例解释

到了第1时刻,三棵小草的高度依次是7,9,59。如果乐滋滋把高度是59的小草剪掉,那么三棵小草高度是7+9+0=16,任务完成。

 

 

第1秒剪第2棵小草,第2秒剪第0棵小草,第3秒剪6棵小草,第4秒剪第5棵小草,第5秒剪第4棵小草。

 

本体是背包+贪心

由于值会变,需要对物品选取的先后顺序排序

毫无疑问   一棵草不可能✄两次 否则无解

另外,如果最后砍得草是已知的(对应题中N=最早时刻),那这题肯定是最后砍长得快的

进而衍生,倘若最后砍的草是确定的,先砍长得慢的

我们用w(i) 表示第i时刻的价值,显然不砍的话,它是单调递增的(grow[i]>0)

所以我们在砍长得第i慢的草时,只有长速为1~i-1 的草有可能被砍,后面的草一定不会砍的

所以它的状态可以由前(i-1)棵草 得到,有单调子结构,可以Dp背包

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXH (1000000+10)
#define MAXN (50+10)
struct grass
{
	int h,g;
	friend bool operator<(const grass a,const grass b){return a.g<b.g;} //先割长得慢的,后割快的
    int w(const int i){return h+i*g;}
}a[MAXN];
int n,H,f[MAXN][MAXN];  //f[i][j]表示 到第i棵 且 取了 j 棵
bool is_ok(int m)
{
	memset(f,0,sizeof(f));

	for (int i=1;i<=n;i++)
		for (int j=1;j<=min(i,m);j++)
		{
			f[i][j]=max(f[i-1][j],f[i-1][j-1]+a[i].w(j));		//如果到第i次时割j稞最优值 就是0/1背包
		}
	int ans=0;
	for (int i=1;i<=n;i++) ans+=a[i].w(m);
	if (ans-f[n][m]<=H)
	{
		cout<<m<<endl;
//		while (1);
		exit(0);
	}


	return 0;
}
int main()
{
	freopen("grass.in","r",stdin);
	freopen("grass.out","w",stdout);
	scanf("%d%d",&n,&H);
	for (int i=1;i<=n;i++) cin>>a[i].h;
	for (int i=1;i<=n;i++) cin>>a[i].g;
	sort(a+1,a+1+n);
	for (int i=0;i<=n;i++) is_ok(i);
	cout<<"-1n";





//	while (1);
	return 0;
}

DNA序列 (各点只出现1次的Dp序)

DNA序列(dna)

题目描述

来自JSSI(Jinkela State Scientific Institute)的科学家们尝试制造一个长度为N并且只包含A的DNA序列,不出意外地失败了。他们得到了一个含有A和B两种部件的序列。现在他们打算对实验结果进行篡改,来得到一个全部是A的序列。篡改的方式有两种:

1 更改某一位上部件的状态(A变成B,B变成A)

2 更改某个前缀内所有部件的状态

两种操作的代价都为1。

你的任务自然是求最小代价。

输入

第一行为N,序列长度。

第二行是一个长度为N且只包含A和B的字符串,表示初始序列。

输出

最小代价。

样例输入

12

AAABBBAAABBB

样例输出

4

数据范围

对于10%的数据,N<=10

对于70%的数据,N<=100000

对于100%的数据,N<=1000000

当一个题目有几种只可能出现1次的操作,而某些操作对后续操作的影响相同,且之后的操作不会对前面影响

便可根据对后续情况影响的状况Dp,乃至D成了贪心

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (1000000+10)
int n;
char s[MAXN];
char turn(const char a)
{
	if (a=='A') return 'B';
	else return 'A';
}
int F[2]={0,1};
int main()
{
	freopen("dna.in","r",stdin);
	freopen("dna.out","w",stdout);
	scanf("%d",&n);
	scanf("%s",s);
	for (int i=n-1;i>=0;i--)
	{
//		cout<<F[0]<<' '<<F[1]<<endl;
		int f0=min(F[0]+(s[i]=='B'),F[1]+1+(s[i]=='B'));
		int f1=min(F[0]+1+(s[i]=='A'),F[1]+(s[i]=='A'));
		F[0]=f0;
		F[1]=f1;
	}
	cout<<min(F[0],F[1])<<endl;


//	while (1);
	return 0;
}