http://www.cnblogs.com/wally/p/3551422.html
poj 1887
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = (100000 + 10);
const int inf = 0x3f3f3f3f;
int n;
int num[MAXN], dp[MAXN];
int Binary_Search(int low, int high, int num)
{
while (low <= high) {
int mid = (low + high) / 2;
if (dp[mid] >= num) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return low;
}
int main()
{
int x, t = 1;
while (~scanf("%d", &x) && x != -1) {
num[n = 0] = x;
while (scanf("%d", &x) && x != -1) {
num[++n] = x;
}
memset(dp, 0, sizeof(dp));
int len = 1;
for (int i = 0; i <= n; i++) {
int pos = Binary_Search(1, len, num[i]);
if (dp[pos] < num[i]) dp[pos] = num[i];
if (pos > len) len++;
}
printf("Test #%d:\n", t++);
printf(" maximum possible interceptions: %d\n\n", len);
}
return 0;
}
O(nlog(n))的做法,还有一个n^2的做法,不再复述
以下是O(nlog(n))求最长上升子序列的做法标准做法
#include <iostream>
using namespace std;
int find(int *a,int len,int n)//若返回值为x,则a[x]>=n>a[x-1]
{
int left=0,right=len,mid=(left+right)/2;
while(left<=right)
{
if(n>a[mid]) left=mid+1;
else if(n<a[mid]) right=mid-1;
else return mid;
mid=(left+right)/2;
}
return left;
}
void fill(int *a,int n)
{
for(int i=0;i<=n;i++)
a[i]=1000;
}
int main(void)
{
int max,i,j,n,a[100],b[100],c[100];
while(cin>>n)
{
fill(c,n+1);
for(i=0;i<n;i++)
cin>>a[i];
c[0]=-1;// …………………………………1
c[1]=a[0];// …………………………2
b[0]=1;// …………………………………3
for(i=1;i<n;i++)// ………………4
{
j=find(c,n+1,a[i]);// …………………5
c[j]=a[i];// ………………………………6
b[i]=j;//……………………………………7
}
for(max=i=0;i<n;i++)// ………………………8
if(b[i]>max)
max=b[i];
cout<<max<<endl;
}
return 0;
}
poj 1157
这是很经典的DP,具体怎样我就不多说了,上代码
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int maxn = 100 + 10;
int val[maxn][maxn];
int dp[maxn][maxn];
int main(){
int v,f;
while(~scanf("%d%d",&f,&v)){
for(int i=1;i<=f;i++){
for(int j=1;j<=v;j++){
int t;
scanf("%d",&t);
val[i][j]=t;
}
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=f;i++){
dp[i][i]=dp[i-1][i-1]+val[i][i];
}
for(int i=1;i<=f;i++)
for(int j=i+1;j<=v;j++){
dp[i][j]=max(dp[i-1][j-1]+val[i][j],dp[i][j-1]);
}
printf("%d\n",dp[f][v]);
}
return 0;
}
dp[i][j]表示前i束花放到前j个花瓶中所获得的最大价值,于是有dp[i][j] = max(dp[i-1][j-1] + value[i][j], dp[i][j-1]) (i <= j <= m)(这是因为对于第i束花,可以选择放在第j个花瓶中,也可以选择放在前j - 1个花瓶中)
稍微要注意一下的就是初始化的问题了,由于是有序的放,因此dp[i][i] = dp[i-1][i-1] + value[i][i](1 <= i <= n)
poj 1088
记忆化搜索(很多人把记忆化搜索归到dp里)
#include <iostream>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 100 + 10;
int dp[maxn][maxn],arr[maxn][maxn];
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int r,c;
bool judge(int x,int y){
if(x<=0||x>r||y<=0||y>c)return false;
return true;
}
int dfs(int x,int y){
if(dp[x][y]!=-1)return dp[x][y];
int ans=0;
for(int i=0;i<4;i++){
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(judge(tx,ty)&&arr[x][y]>arr[tx][ty]){
ans=max(ans,dfs(tx,ty)+1);
}
}
return dp[x][y]=ans;
}
int main(){
while(~scanf("%d %d",&r,&c)){
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)scanf("%d",&arr[i][j]);
int ans=0;
memset(dp,-1,sizeof(dp));
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)ans=max(ans,dfs(i,j)+1);
printf("%d\n",ans);
}
}
poj 1014
这个题可以用贪心+剪枝做,也可以用01背包,01背包那个我看的不是很懂,贪心加剪枝的做法,有个作者讲得很清晰,我就抄过来了
网上很多人说直接采用dp就可以了,但是我觉得不用那么复杂,用贪心算法+回溯+剪枝就可以了,而且特快,运行时间为0.
1.首先,将所有石子价值先加,如果sum为奇数,则返回false.
2.然后,采用贪心策略在石子中选择价值和为sum/2的组合,如果组合成功,返回true,否则false.
a).既然贪心,那当然首先选价值为6的石子,在不大于sum/2的前提下,尽量多选,假设数量为count。然后是价值为5,4,3,。。。
b).所谓回溯,就是当以上不能成功组合时,将count--,既少选一个,然后又是5,4,3,。。。
c).如果仅仅采用以上的步骤,最终答案肯定是正确的,但是肯定也会超时。因为某一价值的石子最大数量为20000,如果count--,得遍历多少啊。所以我们还要采用剪枝算法。
以第一步选择价值为6的石子为例,如果接下去的选择价值为5的石子的数量超过6个,则我们可以用5个价值为6的石子替代;同样,如果再接下去的选择价值为4的石子的个子超过3个,我们也可以用2个价值为6的石子替代,依此类推。于是我们可以得到,在有足够的价值为6的石子的前提下,设选择6的个数为count,那么必须有(sum/2 - count*6)<=(5*5+4*2+3+2*2+5)= 45.也就是回溯时count减小到一定数量不满足以上不等式时就停止,这样将剪去大部分的无用的搜索。当然,接下去的选择5,4,3都如此。
poj 1160
dp[i][j]表示前i个邮局设立在前j个村庄的最短距离和,sum[i][j]表示第i个村庄到第j个村庄建立一个邮局的距离和,显然,该邮局建立在i,j的中点位置最佳,于是我们可以得出:dp[i][j] = min(dp[i][j], dp[i-1][k] + sum[k + 1][j]) ( 1 <=k < j).而sum[i][j] = sum[i][j-1] + pos[j] - pos[(i + j) /2], 最后注意一下初始化就可以了。
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#define inf 0x3f3f3f
using namespace std;
const int maxn = 300 + 10 ;
int arr[maxn];
int dp[maxn][maxn];
int sum[maxn][maxn];
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++){scanf("%d",&arr[i]);}
memset(dp,inf,sizeof(dp));
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)sum[i][j]=sum[i][j-1]+arr[j]-arr[(i+j)/2];//画画图就明白了,这步没错
for(int i=1;i<=n;i++) dp[1][i]=sum[1][i];
for(int i=2;i<=m;i++)
for(int j=i;j<=n;j++)for(int k=i-1;k<j;k++)dp[i][j]=min(dp[i][j],dp[i-1][k]+sum[k+1][j]);
printf("%d\n",dp[m][n]);
}
return 0;
}