您的位置:首页 > 其它

sduacm16级寒假训练 动态规划(一)

2017-02-02 08:49 423 查看


从这套题开始,由一个java选手变成了c++选手 QvQ...


A - 最长递增子序列

 POJ
- 2533                                                  

A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence ( a1, a2,
..., aN) be any sequence ( ai1, ai2, ..., aiK), where 1 <= i1 < i2 < ... < iK <= N.
For example, sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences, e. g., (1, 7), (3, 4, 8) and many others. All longest ordered subsequences are of length 4, e. g., (1, 3, 5, 8). 

Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.

Input
The first line of input file contains the length of sequence N. The second line contains the elements of sequence - N integers in the range from 0 to 10000 each, separated by spaces. 1 <= N <= 1000

Output
Output file must contain a single integer - the length of the longest ordered subsequence of the given sequence.

Sample Input
7
1 7 3 5 9 4 8


Sample Output
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   

A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, ..., xm > another sequence Z = < z1, z2, ..., zk > is a subsequence of X if there exists a strictly
increasing sequence < i1, i2, ..., ik > of indices of X such that for all j = 1,2,...,k, x ij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X
and Y the problem is to find the length of the maximum-length common subsequence of X and Y.

Input
The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.

Output
For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.

Sample Input
abcfbc         abfcab
programming    contest
abcd           mnp


Sample Output
4
2
0


   若xm = yn(最后一个字符相同),则不难用反正法证明:该字符必是X与Y的任一最长公共子序列Z(设长度为k)的最后一个字符,即有zk = xm = yn,且显然有Zk-1∈LCS(Xm-1,Yn-1),即Z的前缀Zk-1是Xm-1与Yn-1的最长公共子序列。此时,问题化归成求Xm-1与Yn-1的LCS(LCS(X,Y))的长度等于LCS(Xm-1,Yn-1)的长度加1)。

   ·若xm≠yn,则亦不难用反证法证明:要么Z∈LCS(Xm-1, Y),要么Z∈LCS(X , Yn-1)。由于zk≠xm与zk≠yn其中至少有一个必成立,若zk≠xm则有Z∈LCS(Xm-1 , Y);类似的,若zk≠yn
则有Z∈LCS(X , Yn-1)。此时,问题化归成求Xm-1与Y的LCS及X与Yn-1的LCS。LCS(X , Y)的长度为:max{LCS(Xm-1 , Y)的长度, LCS(X , Yn-1)的长度}。

 

   由于上述当xm≠yn的情况中,求LCS(Xm-1 , Y)的长度与LCS(X , Yn-1)的长度,这两个问题不是相互独立的:两者都需要求LCS(Xm-1,Yn-1)的长度。另外两个序列的LCS中包含了两个序列的前缀的LCS,故问题具有最优子结构性质考虑用动态规划法。

   也就是说,解决这个LCS问题,你要求三个方面的东西:

   1> LCS(Xm-1,Yn-1)+1;

   2> LCS(Xm-1,Y),LCS(X,Yn-1);

   3> max{LCS(Xm-1,Y),LCS(X,Yn-1)};



/*
题目大意:求最长的公共子序列
解题思路: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 ;
}



C - 01背包

 HDU - 2602 

Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave … 

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 ? 



InputThe first line contain a integer T , the number of cases. 

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.
OutputOne integer per line representing the maximum of the total value (this number will be less than 2 31).
Sample Input
1
5 10
1 2 3 4 5
5 4 3 2 1


Sample Output
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
[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   

Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could
just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so
that each of them gets the same total value. Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two
of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.

Input
Each line in the input file describes one collection of marbles to be divided. The lines contain six non-negative integers n1 , . . . , n6 , where ni is the number of marbles of value i. So, the example from above would be described
by the input-line "1 0 1 2 0 0". The maximum total number of marbles will be 20000. 

The last line of the input file will be "0 0 0 0 0 0"; do not process this line.

Output
For each collection, output "Collection #k:", where k is the number of the test case, and then either "Can be divided." or "Can't be divided.". 

Output a blank line after each test case.

Sample Input
1 0 1 2 0 0
1 0 0 0 1 1
0 0 0 0 0 0


Sample Output
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: