ACM暑假培训——贪心、动态规划专题

5 篇文章 0 订阅

A    

UVA 1422 Processor

题意:

有n个任务,每个任务有三个参数ri,di和wi,表示必须在时刻[ri,di]之内执行,工作量为wi。处理器执行速度可以变化,当执行速度为s时,工作量为wi。处理器的速度可以变化,当执行速度为s时,一个工作量为wi的任务需要执行wi/s个单位时间。任务不一定连续执行,可以分成若干块。求出处理器执行过程中最大速度的最小值。

分析:

看到最大值的最小值,首先想到要用二分枚举答案,而且这个想法也十分正确。

但是问题的难点在于如何判断在某个速度下能否完成所有任务,所以必然要有个优先级,什么样的任务应该先做。可以发现如果现在可以选择执行的任务有多个,那么必然要选择结束时间早的任务先执行,这样才能获得最优解。

所以总体的思路就是,将所有任务按照开始时间排序,二分枚举处理器的速度,然后对每个速度进行判断。按照任务开始时间依次加入优先队列,队列优先级是结束时间早的优先。最后就能判断该速度是否能够执行完所有任务。


#include <algorithm>  
#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <cmath>  
#include <cstdlib>  
#include <queue>  
#define INF 0x7fffffff  
using namespace std;  
struct node  
{  
    int l,r,v;  
} p[2000005],u;  
bool operator < (node a,node b)  
{  
    return a.r>b.r;  
}  
int cmp(node a ,node b)  
{  
    return a.l<b.l;  
}  
int n,L,R,sum;  
  
bool check(int x)  
{  
    int i,t,k=0;  
    priority_queue<node> Q;  
    for(i=L; i<=R; i++)  
    {  
        t=x;  
        while(k!=n&&p[k].l<i) Q.push(p[k++]);  
        while(!Q.empty()&&t!=0)  
        {  
            u=Q.top();  
            Q.pop();  
            if(u.r<i) return false ;  
            if(u.v>t)  
            {  
                u.v-=t;  
                t=0;  
                Q.push(u);  
            }  
            else t-=u.v;  
        }  
    }  
    if(Q.empty()) return true;  
    else return false;  
}  
int main()  
{  
    int T;  
    int i,j;  
    scanf("%d",&T);  
    while(T--)  
    {  
        scanf("%d",&n);  
        for(i=0,sum=0,L=INF,R=0; i<n; i++)  
        {  
            scanf("%d%d%d",&p[i].l, &p[i].r, &p[i].v);  
            R=max(R,p[i].r);  
            L=min(L,p[i].l);  
            sum+=p[i].v;  
        }  
        sort(p,p+n,cmp);  
        int ans=INF;   
        int l,r,mid;  
        l=1,r=sum;  
        while(l<=r)  
        {  
            mid=(l+r)>>1;  
            if(check(mid))  
            {  
                r=mid-1;  
                ans=min(ans,mid);  
            }  
            else l=mid+1;  
        }  
        printf("%d\n",ans);  
    }  
    return 0;  
}  


B

UVA 10905 Children's Game

题意:

给你n个由数字组成的串,将他们排列组成一个串,使该串的值最大。

分析:

第一反应可能是按照字典序排序在前缀相同时取较短的。但是很容易就可以发现反例。比如3和31,或者3和34。所以这样的策略是不正确的。

正确的方式应该是比较a+b和b+a的大小,(’+’是字符串的加法,不是数字的加法)。经过这样比较排序之后的结果一定是最优的。

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#define INF 0x7fffffff
using namespace std;

string s[55];
int cmp(string a,string b)
{
    return a+b>b+a;
}
int main()
{
    int n;
    while(~scanf("%d",&n)&&n)
    {
        string ans;
        for(int i=0;i<n;i++)
            cin>>s[i];
        sort(s,s+n,cmp);
        for(int i=0;i<n;i++)
            ans+=s[i];
        cout<<ans<<endl;
    }
    return 0;
}

C

UVA 10382

题意:

有一块长l宽w的长方形的草坪,草坪中央横向放了n个喷水装置,每个喷水装置有一个半径r,和一个坐标p.问至少需要多少喷水装置才能覆盖草坪。

分析:

先通过几何运算将圆转化为线段,然后这就是线段覆盖区间的问题,贪心策略也是十分明显的,按照线段的终点排序,每次在已覆盖的区域内找出终点最靠后的线段加入。

时间复杂度O(nlogn)

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#define INF 0x7fffffff
#define eps 1e-9
using namespace std;
struct node
{
    double l,r;
}p[100005];

int cmp(node a ,node b)
{
    return a.l<b.l;
}

int main()
{
    int n,l,w,i,j;
    while(scanf("%d%d%d",&n,&l,&w)!=EOF)
    {
        int k=0;
        double s,r;
        for(i=0;i<n;i++)
        {
            scanf("%lf%lf",&s,&r);
            double d=sqrt(r*r-(0.5*w)*(0.5*w));
            if(r*2<w||s+d<0) continue;
            p[k].l=s-d;
            p[k++].r=s+d;
        }
        sort(p,p+k,cmp);
        double L=0,R=0;
        int cnt=0;
        for(i=0;i<k;i++)
        {
            if(p[i].l>R) break;   //不能连续覆盖的情况
            if(R>l) break;        //已经全部覆盖的情况
            if(p[i].l>L) L=R,cnt++;
            R=max(R,p[i].r);
        }
        if(R>=l) printf("%d\n",cnt+1);
        else printf("-1\n");
    }
    return 0;
}

D.

POJ 2948 Martian Mining
题意:
有一个N*M的矩阵,在这个矩阵的每个点上都有两个值Y和B,代表这个点两种矿石的数量。在矩阵的左边是Y矿的收集处,在矩阵上方是B矿的收集处,现在有两种传送带,一种只能向左运输Y矿石,一种只能向上运输B矿石。同种的传送带可以连接。问最终能够获得的最大矿石数目之和。
分析:
首先分别算出两种矿石从每个点运输到收集处所能获得的总数量Sum_B[i][j]和Sum_Y[i][j]。
状态转移程:
Ans[i][j]=max(Ans[i-1][j]+Sum_Y[i][j],Ans[i][j-1]+Sum_B[i][j])
结果就是Ans[n][m]

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#define INF 0x7fffffff
using namespace std;
int B[505][505],Y[505][505];
int dp[505][505];
int main()
{
    int n,m,i,j;
    while(~scanf("%d%d",&n,&m)&&m+n)
    {
        memset(B,0,sizeof(B));
        memset(Y,0,sizeof(Y));
        memset(dp,0,sizeof(dp));
        for(i=1; i<=n; i++)
            for(j=1; j<=m; j++)
                scanf("%d",&Y[i][j]);
        for(i=1; i<=n; i++)
            for(j=1; j<=m; j++)
                scanf("%d",&B[i][j]);
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=m; j++)
            {
                Y[i][j]+=Y[i][j-1];
                B[i][j]+=B[i-1][j];
            }
        }
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
                dp[i][j]=max(dp[i-1][j]+Y[i][j],dp[i][j-1]+B[i][j]);
        printf("%d\n",dp[n][n]);
    }

    return 0;
}


E.

UVA 10534 Wavio Sequence
题意:
给你一个整数组成的串,在它的子序列中找到一个长度为2n+1的串并且前n+1个数字严格递增,后n+1个数字严格递减。求满足条件的串的最大长度。
分析:
首先从前往后求最长递增子序列a[i],然后从后往前求一个最长递增子序列b[i]。那么
Ans=max( min(a[i], b[i]) *2 - 1 )
但是如果用O(n^2)的算法求LIS会超时,所以要用优化的O(nlogn)的算法求LIS。

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#define INF 0x7fffffff
using namespace std;
int up[10005];
int U[10005];
int D[10005];
int down[10005];
int a[10005];

int main()
{
    int i,j,n,m;
    while(~scanf("%d",&n))
    {
        for(i=0; i<n; i++)
            scanf("%d",&a[i]);

        int u,d;
        for(i=1;i<=n;i++) U[i]=D[i]=INF;
        for(i=0; i<n; i++)
        {
            int k=lower_bound(U+1,U+1+n,a[i])-U;
            up[i]=k;
            U[k]=a[i];
            k=lower_bound(D+1,D+1+n,a[n-1-i])-D;
            down[i]=k;
            D[k]=a[n-1-i];
        }
        reverse(down,down+n);
        int Max=0,t=0,ans=0;
        for(i=0; i<n; i++)
        {
            ans=max(ans,min(down[i],up[i]));
        }
        printf("%d\n",ans*2-1);
    }
    return 0;
}


F.

HDU 1052 Tian Ji -- The Horse Racing
题意:
田忌和齐王赛马,他们各有n匹马,每匹马都有个速度,速度大的马能赢得比赛。赢一场+200,平一场+0,输一场-200。问田忌最多能赢多少钱。
分析:
首先考虑依据贪心的思想,考虑每匹马最大的价值是什么。
田忌的快马最大价值就是赢,并且尽量是赢齐王最好的马。因为如果这样损失是最小的。
对于田忌的慢马,它的最大价值也是能赢也要赢,如果能赢齐王最慢的马也要去赢。因为如果田忌的慢马比齐王的慢马块,那么齐王的慢马必输,于是用田忌最慢的马去赢是损失最小的。
如果田忌的慢马不如齐王的慢马,也就是说田忌的慢马是必输的。不论怎样都是输,输也要输的最有价值,所以让田忌最慢的马去和齐王最快的马比。
对于平局的考虑是这道题的难点,到底要不要打平局呢?其实打平局对于贪心来说实际上是吃亏的。因为这不是博弈,双方的智商是不对等的。在你掌握主动权的时候应该尽可能的占便宜,平局时你什么也得不到,实际上是丧失你的主动权和智商优势。所以策略应该是不主动打平局。
所以我的贪心方式:
1. 你的最快的马能赢齐王最快的马,或者你最慢的马能赢齐王最慢的马,赢!
2. 否则,如果你最慢的马不比齐王最慢的马快并且比齐王的最快的马慢,去和齐王最快的马比,输!
3. 否则,平。
其实你会发现进行步骤三的情况只有一种,就是在所有马的速度都相同的时候。

贪心的方法不唯一。

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#define INF 0x7fffffff
using namespace std;
int a[10005];
int b[10005];
int main()
{
    int n,i,j;
    while(scanf("%d",&n)!=EOF&&n)
    {
        for(i=0;i<n;i++)
            scanf("%d",&b[i]);
        for(i=0;i<n;i++)
            scanf("%d",&a[i]);
        sort(a,a+n);
        sort(b,b+n);
        int ai=0,bi=0,aj=n-1,bj=n-1;  
        //a[aj]:田忌快马 a[ai]:田忌慢马
        int ans=0;
        for(i=0;i<n;i++)
        {
            if(a[ai]<b[bi])       //决策1 
                ans+=200,ai++,bi++;
            else if(a[aj]<b[bj])  //决策1 
                ans+=200,aj--,bj--;
            else if(a[aj]>b[bi])  //决策2
                ans-=200,aj--,bi++;
            else bi++,ai++;       //决策3
        }
        printf("%d\n",ans);
    }
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值