最大m子段和问题:给定由n个整数(可能为负)组成的序列a1、a2、a3...,an,以及一个正整数m,要求确定序列的m个不想交子段,使这m个子段的总和最大!
设b(i,j)表示数组a的前j项中i个子段和的最大值,
并且第i个子段包含a[j](1<=i<=m,i<=j<=n),则所求的最优值为maxb(m,j)(m<=j<=n)。在这种定义下b(i,j)的递推公式:b(i,j)=max{b(i,j-1)+a[j],maxb(i-1,t)+a[j](i-1<=t<j)}(1<=i<=m,i<=j<=n);b(i,j-1)+a[j]表示第i个包含a[j-1]和a[j],maxb(i-1,t)+a[j]表示第i个子段仅包含a[j]。
这中定义很强悍,尤其是黄色标记部分,直接把b(i,j)把a[j]限制在第i段内,然后再分a[j-1]和a[j]都在子段内和只有a[j],特殊的当m=1时,b(1,j)=max(b(1,j-1)+a[j],a[j]),1<=j<=n;如果翻译成文字的话,就是说在数组j位置的最大和子段(包含a[j])等于数组在j-1位置的最大和子段(包含a(j-1))加上a[j]和最大和子段只有a[j]的情况的最优值,当然所求解可以表示为maxb(1,j)(1<=j<=n);
其实如果光从b(1,j)=max(b(1,j-1)+a[j],a[j])这个等式本生出发我们很容易的观察出b(1,j-1)的正负直接决定着b(1,j)的取值,然后我们可以产生这中想法,如果b(1,j-1)为正,我就继续加,如果为负我就重新开始加!!!这样的话,写成程序就更简单,其实就是前面我写的最大子段和的动态规划方法的解释。。。(今天终于明白了!!!)
代码如下:
#include<stdio.h>
int MaxSum1(int m,int n,int *a)//m为切割段数,n为数组大小
{
int i,j,k,sum;
if(n<m||m<1)
return 0;
int **b =new int *[m+1];
for(i=0;i<=m;i++)
b[i]=new int[n+1];
for(i=0;i<=m;i++)
b[i][0]=0;
for(j=1;j<=n;j++)
b[0][j]=0;
for(i=1;i<=m;i++)
for(j=i;j<=n-m+i;j++)
{
if(j>i)
{
b[i][j]=b[i][j-1]+a[j];
for(k=i-1;k<j;k++)
{
if(b[i][j]<b[i-1][k]+a[j])
b[i][j]=b[i-1][k]+a[j];
}
}
else
{
b[i][j]=b[i-1][j-1]+a[j];
}
}
sum=0;
for(j=m;j<=n;j++)
if(sum<b[m][j])
sum=b[m][j];
delete b;
return sum;
}
//教科书上又进行了代码优化,如下
int MaxSum(int m,int n,int *a)
{
int i,max,j,sum;
if(n<m||m<1)
return 0;
int *b=new int[n+1];
int *c=new int[n+1];
b[0]=0;
c[0]=0;
for(i=1;i<=m;i++)
{
b[i]=b[i-1]+a[i];
c[i-1]=b[i];
max=b[i];
for(j=i+1;j<=i+n-m;j++)
{
b[j]=b[j-1]>c[j-1]?b[j-1]+a[j]:c[j-1]+a[j];
c[j-1]=max;
if(max<b[j])
max=b[j];
}
c[i+n-m]=max;
}
sum=0;
for(j=m;j<=n;j++)
if(sum<b[j])
sum=b[j];
return sum;
}
int main()
{
int n,m;
int a[100],i;
while(scanf("%d %d",&m,&n)!=EOF)
{
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
printf("%d\n",MaxSum(m,n,a));
}
return 0;
}
对于这段代码我按着思想看了一遍,没有仔细推敲过,不知道会不会是个祸患,但是测试通过了!!!
posted @
2010-09-11 09:48 jince 阅读(661) |
评论 (0) |
编辑 收藏
最大和和矩阵是最大和子段的推广,是最大和子段推广到二维的形式,当然我们可把二维形再转化成一维的形式,这就是求最大和矩阵方法了!二维转化为一维时我们直接用穷举法!
代码如下:
#include<stdio.h>
int MaxSum(int n,int *a)
{
int sum=0,b=0;
for(int i=1;i<=n;i++)
{
if(b>0)
b+=a[i];
else
b=a[i];
if(b>sum) sum=b;
}
return sum;
}
int MaxSum2(int m,int n,int a[][100])
{
int sum=0;
int *b=new int [n+1];
for(int i=1;i<=m;i++)
{
for(int k=1;k<=n;k++)
b[k]=0;
for(int j=i;j<=m;j++)
{
for(int k=1;k<=n;k++)
{
b[k]+=a[j][k];
int max=MaxSum(n,b);
if(max>sum)
sum=max;
}
}
}
delete b;
return sum;
}
int main()
{
int a[100][100],i,j,n,sum,m;
while(scanf("%d %d",&m,&n)!=EOF)
{
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
{
scanf("%d",&a[i][j]);
}
sum=MaxSum2(m,n,a);
printf("%d\n",sum);
}
return 0;
}
运行结果如下:
posted @
2010-09-10 13:09 jince 阅读(516) |
评论 (0) |
编辑 收藏
昨天杭州的天气真的只能用诡异来形容,上班时,万里无云!下班时,半边日出半边雨!我住在杭城西面,下班是由东往西走的,前面 一个大太阳,后面狂风暴雨,坐在公交车上,时而在雨幕里,时而在夕阳下。。更诡异的是我下来车后就开始打雷了,吓的我一路狂奔回家,被雨追着跑的感觉真差!狂奔后,才发现自己原来这么虚弱,才跑了1000米左右,就感觉浑身乏力了,在学校体能测试的时候我可是跑第一的,缺少运动呀,看样子人确实是要常常运动运动,好东西也要常常拾得拾得!
回家后我想起以前我同学问过我的一个问题,为什么是雷声轰隆隆的?我当时回答是云层碰撞不规整,强度有差异,其实这只是我的猜想。然后我同学又问了,为什么雷声是轰隆隆传向远方,然后消失了!我不知道了,没做出回答!!当时被同学小小的鄙视了一下,这个问题其实很多中学生都能回答出来!!!今天我又去网上查了一下,雷是怎么产生的,为什么雷声是轰隆隆的?结果发现了以下文章:
在完成一次闪电的时间内,窄狭的闪电通道上要释放巨大的电能,因而形成强烈的爆炸,产生冲击波,然后形成声波向四周传开,这就是雷声或说"打雷"。
听起来,雷声可以分为两种。一种是清脆响亮,象爆炸声一样的雷声,一般叫做"炸雷";另一种是沉闷的轰隆声,有人叫它做"闷雷"。还有一种低沉而经久不歇的隆隆声,有点儿象推磨时发出的声响。人们常把它叫做"拉磨雷",实际上是闷雷的一种形式。
闪电通路中的空气突然剧烈增热,使它的温度高达15000-20000℃,因而造成空气急剧膨胀,通道附近的气压可增至一百个大气压以上。紧接着,又发生迅速冷却,空气很快收缩,压力减低。这一骤胀骤缩都发生在千分之几秒的短暂时间内,所以在闪电爆发的一刹那间,会产生冲击波。冲击波以5000米/秒的速度向四面八方传播,在传播过程中,它的能量很快衰减,而波长则逐渐增长。在闪电发生后0.1-0.3秒,冲击波就演变成声波,这就是我们听见的雷声。
还有一种说法,认为雷鸣是在高压电火花的作用下,由于空气和水汽分子分解而形成的爆炸瓦斯发生爆炸时所产生的声音。雷鸣的声音在最初的十分之几秒时间内,跟爆炸声波相同。这种爆炸波扩散的速度约为5000米/秒,在之后0.1-0.3秒钟,它就演变为普通声波。
人们常说的炸雷,一般是距观测者很近的云对地闪电所发出的声音。在这种情况下,观测者在见到闪电之后,几乎立即就听到雷声;有时甚至在闪电同时即听见雷声。因为闪电就在观测者附近,它所产生的爆炸波还来不及演变成普通声波,所以听起来犹如爆炸声一般。
如果云中闪电时,雷声在云里面多次反射,在爆炸波分解时,又产生许多频率不同的声波,它们互相干扰,使人们听起来感到声音沉闷,这就是我们听到的闷雷。一般说来,闷雷的响度比炸雷来得小,也没有炸雷那么吓人。
拉磨雷是长时间的闷雷。雷声拖长的原因主要是声波在云内的多次反射以及远近高低不同的多次闪电所产生的效果。此外声波遇到山峰、建筑物或地面时,也产生反射。有的声波要经过多次反射。这多次反射有可能在很短的时间间隔内先后传入我们的耳朵。这时,我们听起来,就觉得雷声沉闷而悠长,有如拉磨之感。
黄色标注部分为雷声轰隆隆的原因,解释的挺容易让人接受的,我现在回忆昨天看到的闪电和听到的雷声,好像是声波不断反射传入我耳朵里的结果,最后声波慢慢消失,就听不到了!
现在我又回想了下当时同学问问题时候场景,如果当时能够从我现在的角度出发,结合到动态规划知识,就可能会回答出问题了。。。首先,最优子结构为声源发出声波传入我耳朵里(这个很明显能知道);然后声波遇到障碍物反射(其实可以看做新的声源),再次传入我的耳朵,不过音量会变小(这个过程和最优子结构性质是相同,当然能够想到这一步才能回答出同学问题,但是从最优子结构是可以猜想后来雷声轰隆隆的);最后就有不同的反射声波传入我的耳朵,听到轰隆隆混杂的声音!
可惜没有如果,不过我现在知道了也好,虽然我同学当时没有告诉我原因,但是他告诉我怎么测声源的位置,声音的速度是340米每秒,从闪电发光的时候,开始数秒设为t,340*t就是数秒人与声源的距离了(解释是声速与光速差太多了,所以光传入眼睛的时间可以看做是声源发出声音的时间,这也是为什么打雷的时候先看到闪电,后听到雷声的原因)。
解释了这些,我还有一点不清楚,就是经常听见飞机在天上飞,但是一抬头飞机却在另一个地方!!!
posted @
2010-09-10 11:25 jince 阅读(1849) |
评论 (0) |
编辑 收藏
摘要: 均分纸牌
Time Limit:1000MS Memory Limit:65536KTotal Submit:241 Accepted:103
Description
有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若干张纸牌,然后移动。移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在...
阅读全文
posted @
2010-09-09 13:00 jince 阅读(2887) |
评论 (2) |
编辑 收藏
弄了一个早上的最接近点对,没弄明白。。。还是做点简单的吧!
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<iostream>
using namespace std;
int main()
{
int magic,i;
srand(time(NULL));//srand()需包含头文件stdlib.h,种子!
printf("RAND_MAX=%d\n",RAND_MAX);//原来RAND_MAX是个常量32767;rand()函数的返回值范围:0~32767
for(i=1;i<10;i++)//输出十个1~100随机数
{
magic=rand()/(int)(((unsigned)RAND_MAX+1)/100);
// magic=rand()%100+1;//课堂上老师说这样可以取1~100之间的随机数,今天才明白原来是跟100取余的结果!
printf("%d ",magic);
}
printf("\n");
double a[10];
for(i=0;i<10;i++)
a[i]=(double)rand()/RAND_MAX;//这样写可以变成小数!
for(i=0;i<10;i++)
printf("%.2lf ",a[i]);
printf("\n");
return 0;
}
posted @
2010-09-08 12:43 jince 阅读(369) |
评论 (2) |
编辑 收藏
简单的问题串联可以组成复杂!
给定已经排好序的n个元素a[0...n-1],现在在这n个元素中找出一特定元素x。
代码如下:
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
template <class Type>
int BinarySearch(Type a[],Type& x,int n)
{
int left=0;
int right=n-1;
while(left<=right)
{
int middle=(left+right)/2;
if(x==a[middle])
return middle;
if(x>middle)
left=middle+1;
else
right=middle-1;
}
return -1;
}
template <class Type> //重载
int BinarySearch(Type a[],Type& x,int left,int right)
{
int middle=(left+right)/2;
if(x==a[middle])
return middle;
else if(x>middle)
return BinarySearch(a,x,middle+1,right);
else
return BinarySearch(a,x,left,middle-1);
return -1;
}
int main()
{
int i,n,x;
int a[100];
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
scanf("%d",&a[i]);
sort(a,a+n);
for(i=0;i<n;i++)
printf("%d ",a[i]);
printf("\n");
scanf("%d",&x);
printf("%d\n",BinarySearch(a,x,n));
printf("%d\n",BinarySearch(a,x,0,n-1));
}
return 0;
}
运行结果:
posted @
2010-09-07 22:42 jince 阅读(197) |
评论 (0) |
编辑 收藏
我们不断做着把事情翻译成语言的工作!
汉诺塔老生常谈的问题!
个人体会:一、理解函数意义和数据意义!
二、想象力,就像把一棵二叉树先展开,再收拢!
直接上代码(注释以前写的):
#include<stdio.h>
int count1,count;//count1,表示第一个圆盘移动的次数
//count,表示总的移动次数
/**//*函数功能:限制移动过程
函数参数: 整形变量num,表示第n个盘子
自发性变量 from,表示源木桩
字符型变量to,表示目的木桩
*/
void move(int num,char from,char to)
{
printf("move %d:from %c to %c\n",num,from,to);
count++;
}
/**//*函数功能:将第n个盘子从源木桩A,借助于木桩C移动到木桩B
函数参数:n,表示第n个盘子
a,表示源木桩a
b,表示目的木桩b
c,表示过度木桩c*/
/**//*我自己想法:
三个木桩,从第n个盘开始移动,只有当其他的盘都在c木桩才能移动n,
同理要将第n-1个盘从c移动到b,要将n-2个盘全部移动到a木桩*/
void hanoi(int n,char a,char b,char c)
{
if(n==1) //递归出口:为什么是n==1?
{
move(n,a,b); //第n个盘子由a->b
count1++;
}
else
{
hanoi(n-1,a,c,b); //将第n-1个盘子,借助于b由a->c
move(n,a,b); //第n个盘子有a->b
hanoi(n-1,c,b,a); //将第n-1个盘子,借助于a有c->b
}
}
int main()
{
int n;
while(n!=0)
{
scanf("%d",&n);
hanoi(n,'A','B','C');
printf("count=%d\n",count);
printf("count1=%d\n",count1);
}
return 0;
}
/**//*通过程序运行,我们还可以得到:
当n为偶数时,第一个盘第一次移动移动到c,
当n为奇数时,第一个盘第一次移动到移动到b*/
运行结果:
posted @
2010-09-07 21:56 jince 阅读(220) |
评论 (0) |
编辑 收藏
有n=2^k个运动员比赛,现要求设计一张比赛日程表,要求如下:
(1)每个选手必须与其他n-1个选手各比赛一次;
(2)每个选手一天只能比赛一次;
(3)循环赛一共进行n-1天;
这个题目自己列下表,观察一下规律就成,书上说的二分不是那么好理解!重在实际应用。。。
代码如下:
#include<stdio.h>
void Table(int k,int a[][100])
{
int n=1;
int i,j,s,t;
for(i=1;i<=k;i++)
n*=2;
for(i=1;i<=n;i++)
a[1][i]=i;
int m=1;
for(s=1;s<=k;s++)
{
n/=2;
for(t=1;t<=n;t++)
for(i=m+1;i<=2*m;i++)
for(j=m+1;j<=2*m;j++)
{
a[i][j+(t-1)*m*2]=a[i-m][j+(t-1)*m*2-m];
a[i][j+(t-1)*m*2-m]=a[i-m][j+(t-1)*m*2];
}
m*=2;
}
}
int main()
{
int a[100][100];
Table(3,a);
int i,j;
for(i=1;i<=8;i++)
{
for(j=1;j<=8;j++)
printf("%d ",a[i][j]);
printf("\n");
}
return 0;
}
结果:
posted @
2010-09-07 20:53 jince 阅读(232) |
评论 (0) |
编辑 收藏
最大公约的两种求法:
代码如下:
#include<stdio.h>
int gcds(int a,int b)//stein算法
{
if(a<b){
int temp = a;
a = b;
b=temp;
}
if(0==b) //b=0返回
return a;
if(a%2==0 && b%2 ==0) //a和b都是偶数
return 2*gcds(a/2,b/2);
if ( a%2 == 0) // a是偶数,b是奇数
return gcds(a/2,b);
if ( b%2==0 ) // a是奇数,b是偶数
return gcds(a,b/2);
return gcds((a+b)/2,(a-b)/2);// a和b都是奇数
}
int gcdo(int a,int b) //欧几里得
{
if(b==0)
return a;
if(a<b){
int temp = a;
a = b;
b=temp;
}
return gcdo(b,a%b);
}
int main()
{
int a,b;
while(scanf("%d %d",&a,&b)!=EOF)
{
printf("%d\n%d\n",gcds(a,b),gcdo(a,b));
}
return 0;
}
欧几里得算法,当初老师给我们的时候,只是说这样求可以求得最大公约数,没给出证明。。
百度上搜了一下,基本证明思想a,b(a>b)的公约数和a,a%b的公约数是相同的。Stein算法证明也类似!!
posted @
2010-09-07 12:09 jince 阅读(768) |
评论 (0) |
编辑 收藏
苦恼啊!
Description
设有n个正整数,将他们连接成一排,组成一个最大的多位整数。例如:n=3时,3个整数13,312,343,连成的最大整数为:34331213
又如:n=4时,4个整数7,13,4,246连接成的最大整数为7424613
Input
N
N个数
Output
连接成的多位数
Sample Input
3
13 312 343
0
Sample Output
34331213
这个题目意思应该很好理解,至少会想有两种解题思路,
一、把数组从大到小排列,这样是最大吗? 显然不是,例如:123 9,应该输出9123;
二、把数组按字符串排序,这样是最大吗?这问题可能会然我们困惑,但是这也是错的,例如:120,12;应该输出12120;
这个题目不知道毒害了多少人(当然我指的是ACM新人),尤其是第二种思路去写代码的。。这只是一个悲剧的开始,你把一条弯路在直走!
其实这个题目应该是属于动态规划的最简单应用!子问题,给你两个数,让你连成最大数,一定非常简单,只能组成两个数,比较一下大小就成!这就是解题思路!
如果给你三个数呢?一直递推下去!悲剧啊,尽然怎么简单!
代码如下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int n,i,j,l,k;
char s[1000][100];
char s1[200],s2[200];
while(scanf("%d",&n)!=EOF&&n!=0)
{
for(i=0;i<n;i++)
{
scanf("%s",s[i]);
while(s[i][0]=='0')
{
if(strlen(s[i])==1)
break;
strcpy(s[i],s[i]+1);
}
}
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)
{
strcpy(s1,s[i]);
strcat(s1,s[j]);
strcpy(s2,s[j]);
strcat(s2,s[i]);
if(strcmp(s1,s2)<0)
{
strcpy(s1,s[i]);
strcpy(s[i],s[j]);
strcpy(s[j],s1);
}
}
}
for(i=0;i<n;i++)
{
if(s[0][0]=='0')
{
printf("0");
break;
}
else
{
printf("%s",s[i]);
}
}
printf("\n");
}
return 0;
}
本来我也是第二种思路的,我看了感觉不像,因为以前写过了!
早上的几点收获:
1、<string>头文件和<string.h>头文件是不一样的!运行时的一个错误,弄了一个早上,最后发现这什么个问题!
2、运用容器,排序字符串
vector<string> words;
string str;
while(scanf("%s",str)!=EOF)
words.push_back(str);
sort(words.begin(),words.end(),greater<string>());
for(vector<string>::size_type i=0;i<words.size();i++)
{
cout<<words[i]<<endl;
}
不过会有很多警告,让我很苦恼!有谁知道呢?
警告如下:
--------------------Configuration: rongqi - Win32 Debug--------------------
Compiling
rongqi.cpp
c:\documents and settings\administrator\桌面\rongqi\rongqi.cpp(81) : warning C4786: 'std::reverse_iterator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const *,std::basic_string<char,std::char_traits<char>,std::allocator<char
> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const *,int>' : identifier was truncated to '255' characters in the debug information
c:\documents and settings\administrator\桌面\rongqi\rongqi.cpp(81) : warning C4786: 'std::reverse_iterator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::basic_string<char,std::char_traits<char>,std::allocator<char> >,st
d::basic_string<char,std::char_traits<char>,std::allocator<char> > &,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,int>' : identifier was truncated to '255' characters in the debug information
c:\program files\microsoft visual studio\vc98\include\vector(39) : warning C4786: 'std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
>::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >' : identifier was truncated to '255' characters in the debug information
c:\program files\microsoft visual studio\vc98\include\vector(60) : warning C4786: 'std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
>::~vector<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >' : identifier was truncated to '255' characters in the debug information
rongqi.obj - 0 error(s), 0 warning(s)
3、在程序头上写#pragma warning (disable: 4786),可以注销4786以后警告!
posted @
2010-09-06 11:52 jince 阅读(871) |
评论 (0) |
编辑 收藏