从这套题开始,由一个java选手变成了c++选手 QvQ...
A - 最长递增子序列
POJ - 2533
Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.
7 1 7 3 5 9 4 8
4
/*
题目大意:给一个序列,求出最长上升子序列的长度
解题思路:LIS(Longest increasing subsequence) 状态转移方程f[i]=max(f[i],f[j]+1) O(n^2)
*/
#include <cstdio>
#include <algorithm>
using namespace std;
int n,num[10000],f[10000];
int main()
{
scanf("%d",&n);
for(int i = 0;i<n;i++)
{
scanf("%d",&num[i]); //num[]记录每个数字
f[i] = 1; //初始是1
}
for(int i = 1;i<n;i++)
for(int j = 0;j<i;j++)
{
if(num[j]<num[i]) //如果前一位<此位 则当前序列长度加一,否则当前序列长度没变,为之前最长子序列的长度f[i]
f[i] = max(f[i],f[j]+1); //0 2 3 1 4反证 f[i]=f[j]+1不成立 需要f[i]保存目前最长的
}
int ans = 0;
for(int i= 0;i<n;i++) //最后遍历一遍,找到答案
ans = max(ans,f[i]);
printf("%d",ans);
return 0;
}
贴一个O(nlogn)的算法,加了个二分搜索,用stack实现
这个算法的具体操作如下(by RyanWang):
开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。
这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的''潜力''增大了。
举例:原序列为1,5,8,3,6,7
栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。
我想,当出现1,5,8,2这种情况时,栈内最后的数是1,2,8不是正确的序列啊?难道错了?
分析一下,我们可以看出,虽然有些时候这样得不到正确的序列了,但最后算出来的个数是没错的,为什么呢?
想想,当temp>top时,总个数直接加1,这肯定没错;但当temp<top时呢? 这时temp肯定只是替换了栈里面的某一个元素,所以大小不变,就是说一个小于栈顶的元素加入时,总个数不变。这两种情况的分析可以看出,如果只求个数的话,这个算法比较高效。但如果要求打印出序列时,就只能用DP了。
#include <iostream>
#define SIZE 1001
using namespace std;
int main()
{
int i, j, n, top, temp;
int stack[SIZE];
while(cin >> n)
{
top = 0;
/* 第一个元素可能为0 */
stack[0] = -1;
for (i = 0; i < n; i++)
{
cin >> temp;
/* 比栈顶元素大数就入栈 */
if (temp > stack[top])
{
stack[++top] = temp;
}
else
{
int low = 1, high = top;
int mid;
/* 二分检索栈中比temp大的第一个数 */
while(low <= high)
{
mid = (low + high) / 2;
if (temp > stack[mid])
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
/* 用temp替换 */
stack[low] = temp;
}
}
/* 最长序列数就是栈的大小 */
cout << top << endl;
}
return 0;
}
B - 最长公共子序列
POJ - 1458
abcfbc abfcab programming contest abcd mnp
4 2 0
/*
题目大意:求最长的公共子序列
解题思路:LCS(longest common subsequence)
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
char s1[1000],s2[1000];
int dp[1000][1000];
int len1,len2;
void LCS()
{
int i, j;
memset(dp,0,sizeof(dp));
for(int i = 1;i<=len1;i++)
for(int j = 1;j<=len2;j++)
{
if(s1[i-1]==s2[j-1])
dp[i][j] = dp[i-1][j-1]+1;
else
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
}
int main()
{
while(scanf("%s%s",&s1,&s2)!=EOF)
{
len1 = strlen(s1); len2 = strlen(s2);
LCS();
printf("%d\n",dp[len1][len2]);
}
return 0 ;
}
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.
1 5 10 1 2 3 4 5 5 4 3 2 1
14
/*
题目大意:有一个人喜欢搜集骨头,每个骨头有value和volume,给定bag容量求value最大值。
解题思路:01背包
*/
#include<stdio.h>
#include<string.h>
#define M 1009
typedef struct pack
{
int cost;
int val;
}PACK;
int f[M][M];
int main()
{
int cas,n,v,i,j;
PACK a[M];
scanf("%d",&cas);
while(cas--)
{
scanf("%d%d",&n,&v);
memset(f,0,sizeof(f));
for(i=1;i<=n;i++)
scanf("%d",&a[i].val);
for(i=1;i<=n;i++)
scanf("%d",&a[i].cost);
for(i=1;i<=n;i++) //此处用的是二维数组,一维实现可改为 for(int i = v;i>=0;i--) f[i]=max(f[i],f[i-cost]+val);
for(j=0;j<=v;j++)
if(j-a[i].cost>=0&&f[i-1][j]<f[i-1][j-a[i].cost]+a[i].val)
f[i][j]=f[i-1][j-a[i].cost]+a[i].val;
else
f[i][j]=f[i-1][j];
printf("%d\n",f[n][v]);
}
return 0;
}
D - 完全背包
POJ - 1384
/*
题目大意:上一套题的原题...
解题思路:完全背包,上个是java的,这里写了个C++的
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int T,emp,filled,type;
int wei[1000000];
int val[1000000];
int bag[1000000];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&emp,&filled,&type);
int weight = filled-emp;
for(int i = 0;i<type;i++)
scanf("%d%d",&val[i],&wei[i]);
for(int i = 0;i<=weight;i++)
bag[i] = 1000000;
bag[0] = 0;
// cout<<bag;
for(int i= 0;i<type;i++)
for(int j = wei[i];j<=weight;j++)
{
if( bag[j] < bag[j-wei[i]] + val[i] )
bag[j] = bag[j];
else
bag[j] = bag[j-wei[i]] + val[i];
// cout<<i<<" "<<j<<" "<<wei[i]<<" "<<val[i]<<" ";
// cout << bag[j]<<endl;
}
if(bag[weight]==1000000)
printf("This is impossible.\n");
else
printf("The minimum amount of money in the piggy-bank is %d.\n",bag[weight]);
}
}
E - 多重背包
POJ - 1014
The last line of the input file will be "0 0 0 0 0 0"; do not process this line.
Output a blank line after each test case.
1 0 1 2 0 0 1 0 0 0 1 1 0 0 0 0 0 0
Collection #1: Can't be divided. Collection #2: Can be divided.
/*
题目大意:有价值1-6的若干石头,问两人能否实现平分价值。000000代表输入结束
解题思路:多重背包,本题无明显背包容量,所以定义容量为总价值的一半,装满即成功平分
*/
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int bag[1000000];
int tar; //total value/2
void bag01(int c, int v)
{
for(int i = tar;i>=c;i--)
{
if(bag[i]<bag[i-c]+v)
{bag[i] = bag[i-c]+v;}
}
}
void bag02(int c,int v)
{
for(int i = c;i<=tar;i++)
{
if(bag[i]<bag[i-c]+v)
bag[i] = bag[i-c]+v;
}
}
void bag03(int c,int v,int n)
{
if(c*n>=tar) //花费*件数大于容量时,视为完全背包
bag02(c,v);
else //否则为01背包 将件数通过二进制思想转化为不同价值的若干物品方法是:将第i种物品分成若干件物品,
//其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。
//使这些系数分别为 1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。
//例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。
//分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。
//另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示,
{
int k = 1;
while(k<=n)
{
bag01(k*c,k*v);
n -=k;
k *=2;
}
}
bag01(n*c,n*v);
}
int main()
{
int n[6],sum,i,k=1;
while( scanf("%d%d%d%d%d%d",&n[0],&n[1],&n[2],&n[3],&n[4],&n[5]) , n[0]+n[1]+n[2]+n[3]+n[4]+n[5])
{
memset(bag,0,sizeof(bag));
sum = n[0]+2*n[1]+3*n[2]+4*n[3]+5*n[4]+6*n[5];
// cout<<sum<<endl;
if(sum%2!=0) //无法平分
{
printf("Collection #%d:\nCan't be divided.\n\n",k++);
continue;
}
tar = sum/2;
for(int i = 0;i<6;i++)
{
if(n[i])
bag03(i+1,i+1,n[i]);
}
if(tar==bag[tar])
printf("Collection #%d:\nCan be divided.\n\n",k++);
else //无法填满背包
printf("Collection #%d:\nCan't be divided.\n\n",k++);
}
return 0;
}