先来个链接,按上面的来做吧

本文解析了多个经典动态规划(DP)题目,包括最长上升子序列、花瓶摆放问题、记忆化搜索等,提供了详细的代码实现及算法思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值