【ACM训练二】贪心算法与动态规划

贪心算法

贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,它所做出的是在某种意义上的局部最优解。(子问题最优解)(每次做出决策后问题规模变小)
相邻交换—不干扰其他—局部最优——排序——得最值

相关知识点

定义结构体(通过 . 访问属性)

struct object
{
    int v,w;
}

排序

#include <cstdio.h>
#include <algorithm>
using namespace std;
int a[10];
int main()
{
    for(int i=0;i<5;i++) scanf("%d",&a[i]);
    sort(a,a+5);    默认从小到大排
    ...
}

对于新的数据类型,需要定义运算符

struct object
{
    int v,w;
    bool operator 〈(const object &t) const 
   { if (v*t.w < t.v*w) return true; //避免了除的问题
   else return false; 
    }
} a[10];
例题

1、作业调度问题
伊格内修斯刚从第30届ACM/ICPC毕业。现在他有很多家庭作业要做。每个老师都给他一个交作业的最后期限。如果伊格内修斯在最后期限后交作业,老师就会降低期末考试的分数。现在我们假设每个人做作业都要花一天的时间。所以伊格内修斯希望你能帮他安排作业的顺序,把减少的分数降到最低。
输入包含几个测试用例。输入的第一行是一个整数T,它是测试用例的数量。接下来是T测试用例。
每个测试用例都以一个正整数N(1<=N<=1000)开头,表示家庭作业的数量。然后是两行。第一行包含N个整数,表示主题的截止日期,下一行包含N个整数,表示降低的分数。
对于每个测试用例,您应该输出最小的总减少分数,每个测试用例一行。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct object
{
	int l,d;
	bool operator < (const object &t) const
	{
		if(t.l<l) return true;
		else return false;
	} 
}a[1010];
int bj[1000];
int main()
{
	int num;
	scanf("%d",&num);
	int i,j,n;
	while(num--)
	{
		scanf("%d",&n);
		
		for(j=0;j<n;j++)
		scanf("%d",&a[j].d);
		for(j=0;j<n;j++)
		scanf("%d",&a[j].l);
		
		sort(a,a+n);
		memset(bj,0,sizeof(bj));
		int sum=0;
        
		for(i=0;i<n;i++)
		{
			j=a[i].d;
			while(j)
			{
			if(bj[j]==0)
			{
				bj[j]=1;
				break;
			}
			j--;
		    }
		    
			if(j==0)
			sum+=a[i].l;
		}
		printf("%d\n",sum);
	}
	return 0;
}

2、勇士斗恶龙
题意: 有m个勇士和n个怪兽,每个怪兽有一个防御值,每个勇士有一个能力值,雇佣一个能力值为x的勇士杀死一个防御力不超过x的怪兽,且需要支付x金币。如何雇佣勇士才能够杀死所有怪兽,且耗费的金币最少?如果无解,输出“Loowater is doomed!”
思路: 排序+贪心。 先把龙头跟骑士身高各自从小到大排序, 然后从头到尾比较。 若能砍下则雇佣, 否则用下一个骑士。 直到龙头砍完, 或者骑士用完。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int mx=20009;
int a[mx],b[mx],n,m;
int main(){
	while(~scanf("%d%d",&n,&m)&&(n|m)){
		long long ans=0;
		for(int i=0;i<n;++i)
		scanf("%d",a+i);
		for(int i=0;i<m;++i)
		scanf("%d",b+i);
		sort(a,a+n);
		sort(b,b+m);
		int i=0;
		for(int j=0;j<m&&i<n;j++){
			if(a[i]<=b[j]){
				i++;
				ans+=b[j];
			}
		}
		if(i==n)
		printf("%lld\n",ans);
		else
		puts("Loowater is doomed!");
	}
	return 0;
}

3、最大承受力
每层楼都有自己的重量wi和强度si。当叠加层,每层都有PDV(潜在的损害价值)等于(Σwj) si(Σwj)代表楼层上面的全部重量。使得最大PDV最小
有几个测试用例。在每个测试用例中,第一行是一个整数N (1 <= N <= 105),表示建筑物的楼层数。下面N行指定楼层。它们每个都包含两个由单个空格分隔的整数wi和si (0 <= wi, si <= 100000)。请处理到EOF(文件结束)。对于每个测试用例,您的程序应该在一行中输出一个整数——整个建筑的最小PDV。
如果在最佳配置中没有地板损坏(也就是说,最小PDV是非正的),则输出0。

#include <math.h>
#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
struct node{
    LL a,b;
}p[100010];
int cmp(node u,node v){
    return u.a+u.b<v.a+v.b;
}
int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        for(int i=0;i<n;i++)
          scanf("%I64d%I64d",&p[i].a,&p[i].b);
        sort(p,p+n,cmp);
        LL res=0,_max=0;
        for(int i=0;i<n;i++){
            _max=max(_max,res-p[i].b);
            res+=p[i].a;
        }
        printf("%I64d\n",_max);
    }
}

动态规划

大问题化为小问题,利用小问题的最优解得出大问题的最优解

例题

1、01背包问题
 一个贼在偷窃一家商店时发现了n件物品,其中第i件值vi元,重wi磅。他希望偷走的东西总和越值钱越好,但是他的背包只能放下W磅。请求解如何放能偷走最大价值的物品,这里vi、wi、W都是整数。
解法:
  如果每个物品都允许切割并只带走其一部分,则演变为部分背包问题,可以用贪心法求解。0-1背包问题经常作为贪心法不可解决的实例(可通过举反例来理解),但可以通过动态规划求解。
  为了找出子结构的形式,粗略地分析发现,对前k件物品形成最优解时,需要决策第k+1件是否要装入背包。但是此时剩余容量未知,不能做出决策。因此把剩余容量也考虑进来,形成的状态由已决策的物品数目和剩余容量两者构成。这样,所有状态可以放入一个n*(W+1)的矩阵c中,其值为当前包中物品总价值,可得递推公式,代码如下

#include <stdio.h>
#include <stdlib.h>

int package_dp(int *v,int *w,int n,int total) {
    int i,j,tmp1,tmp2;
    int **c = (int **)malloc((n+1)*sizeof(int *));
    for(i=0;i<n+1;i++)
        c[i]=(int *)malloc((total+1)*sizeof(int));
    for(i=0,j=0;j<total;j++)
        c[i][j] = 0;
    for(i=1;i<=n;i++) {
        c[i][0] = 0;
        for(j=1;j<=total;j++) {
            if(w[i]>j)
                c[i][j] = c[i-1][j];
            else {
                tmp1 = v[i]+c[i-1][j-w[i]];
                tmp2 = c[i-1][j];
                c[i][j]=(tmp1>tmp2?tmp1:tmp2);
            }
        }
    }
    printf("c[%d][%d]:%d\n",n,total,c[n][total]);
    return 0;
}

int main() {
    int v[] = {0,10,25,40,20,10};
    int w[] = {0,40,50,70,40,20};
    int total = 120;
    package_dp(v,w,sizeof(v)/sizeof(int)-1,total);
    return 0;
}

2、最长公共子序列、
对于序列S和T,求它们的最长公共子序列。例如X={A,B,C,B,D,A,B},Y={B,D,C,A,B,A}则它们的lcs是{B,C,B,A}和{B,D,A,B}。求出一个即可。
解法:
  和2类似,对于X[1…m]和Y[1…n],它们的任意一个lcs是Z[1…k]。
  (1)如果X[m]=Y[n],那么Z[k]=X[m]=Y[n],且Z[1…k-1]是X[1…m-1]和Y[1…n-1]的一个lcs;
  (2)如果X[m]!=Y[n],那么Z[k]!=X[m]时Z是X[1…m-1]和Y的一个lcs;
  (3)如果X[m]!=Y[n],那么Z[k]!=Y[n]时Z是X和Y[1…n-1]的一个lcs;

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000;
char a[N],b[N];
int dp[N][N];
int main()
{
    int lena,lenb,i,j;
    while(scanf("%s%s",a,b)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        lena=strlen(a);
        lenb=strlen(b);
        for(i=1;i<=lena;i++)
        {
            for(j=1;j<=lenb;j++)
            {
                if(a[i-1]==b[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        printf("%d\n",dp[lena][lenb]);
    }
    return 0;
}

未完待续

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值