動態規劃測試3(test20170406)

前言

突然就考試,做完1,2題就開始浪了。於是就第三題打了個暴力…
用Emacs之後代碼縮進似乎就呵呵了…

操練(Training.pas/c/cpp Time:1s Memory:256M

【問題描述】

高老大有一個NN 的廣場,被均分成NN 個格子。其中某些格子種上了樹。
爲了維護世界的和平,爲了貫徹和平與發展的原則,老大不得不開始操練部下了。部下們必須站在廣場中某沒種上樹個格子中,而且一個格子內不允許有兩個人站着。同時,爲了顯得整齊劃一,部下們要維護一個方陣的陣型,也就是N*N的廣場內的一個XY 的矩形(該矩形的長和寬必須與廣場的長和寬平行),每個格子上都必須有一個部下。現在老大想知道,一次最多操練多少個部下?

【輸入】

輸入文件名爲Training.in。
輸入第一行兩個正整數N和M,代表廣場的長和寬。
下接一個N行M列的字符矩陣,若第I行第J列爲‘0’則代表該格子上有一顆樹。

【輸出】

輸出文件名爲Training.out。
輸出一次最多能夠操練的部下個數。

【輸入輸出樣例】

Training.in
2
11
11
Training.out
4

【數據範圍】

對於80%的數據,N250
對於100%的數據,N1000

【題解】

這道題目就是極大化子矩形 的一個模板吧,所以還好做吧。
這個題目的母題是USACO中的rectbarn,裏面講的很詳細。
解釋一下我這裏f[i][j] 表示的是這個點前面1的個數,那麼你應該就看的懂了吧。。。

【代碼】

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))

const int size = 1000+10;
int n,ans;
int f[size][size];
char ch[size][size];
int minn,cnt;

inline int read() {
    int f=1,in=0;
    char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())
    if(ch=='-')
        f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())
    in=in*10+ch-'0';
    return in*f;
}

int main() {
    freopen("training.in","r",stdin);
    freopen("training.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)
    scanf("%s",ch[i]+1);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        if(ch[i][j]=='1') f[i][j]=f[i][j-1]+1;
    /*for(int i=1;i<=n;i++) {
    for(int j=1;j<=n;j++) 
        printf("%d ",f[i][j]);
    puts("");
    }*/
    for(int i=n;i>=1;i--)
    for(int j=1;j<=n;j++) {
        minn=f[j][i];
        cnt=1;
        if(minn*(n-j+1)<=ans) continue;
        ans=Max(ans,cnt*minn);
        for(int k=j+1;k<=n;k++) {
        minn=Min(minn,f[k][i]);
        cnt++;
        if(minn*(n-j+1)<=ans) break;
        ans=Max(ans,cnt*minn);
        }
    }
    printf("%d\n",ans);
    return 0;
}   

炸彈(Bomb.pas/c/cpp Time:1s Memory:256M)

【問題描述】

高老大又在鼓搗炸彈了,這不,啓動了一個了。英明英勇的老大又在跟他的部下們表演空手光速拆炸彈了。
炸彈上是一個圓盤,圓盤上順時針寫着N個數。其實這個炸彈就是要求從中選擇若干個連續的數(注意每個數最多隻能選一次)加起來,使得這些數字的和最大,然後輸入這個
最大的和,計時就會停止。現在你被老大要求上臺表演,時間是1s,任務就交給你了。

【輸入】

輸入文件名爲Bomb.in。
輸入第一行包含一個正整數N,表示數字的個數。
第二行包含N個整數,爲所給的數字。

【輸出】

輸出文件名爲Bomb.out。
輸出包含一個整數,爲最大的可以得到的和。

【輸入輸出樣例】

Bomb.in
8
2 -4 6 -1 -4 8 -1 3
Bomb.out
14

【數據範圍】

對於30%的數據 1N200 ;
對於70%的數據 1N10000 ;
對於100%的數據 1N100000 , 答案在longint範圍內。

【題解】

這個題目可以很明顯的發現就是求最大連續子序列的題目
然後我們可以一般輕鬆的求出在非環上的最大連續子序列,O(n) 出界
然後就是處理此時在環上的情況了。。。
我們將一個方案分成兩部分,一部分從 1 開始遞增,一部分從 n 開始遞減。
然後用dp就可以求出末端不超過 i 且從 1 或 n 出發的最大連續子序列。
然後就沒有然後了…

【代碼】

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
typedef int LL;
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))

const int size = 200000+10;
LL n,ans;
LL a[size],f[size];

inline LL read() {
    LL f=1,in=0;
    char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())
    if(ch=='-')
        f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())
    in=in*10+ch-'0';
    return in*f;
}

inline LL erfen(LL l,LL r) {
    if(l==r) return f[l];
    LL mid=(l+r)>>1;
    return Min(erfen(l,mid),erfen(mid+1,r));
}

int main() {
    freopen("bomb.in","r",stdin);
    freopen("bomb.out","w",stdout);
    n=read();
    for(LL i=1;i<=n;i++)
    a[i]=a[i+n]=read();
    LL m=n<<1;
    for(LL i=1;i<=m;i++)
    f[i]=f[i-1]+a[i];
    LL minn=0;
    for(LL i=n;i<=m;i++) {
    if(f[i-n-1]==minn) minn=erfen(i-n,i);
    else minn=Min(minn,f[i]);
    ans=Max(ans,f[i]-minn);
    }
    printf("%d\n",ans);
    return 0;
}

戰爭(War.pas/c/cpp Time:1s Memory:256M)

【問題描述】

高老大要打團戰了。他要召集N(N是奇數)個人去組織一場戰爭。現在高老大的手下有T個人,每個人都有一個戰鬥力值和影響力值。當這N個人的影響力值之和超過M的時候,必然會引起巨大的社會動盪。爲了愛與和平,老大明智地決定,不引起巨大的社會動盪。同時,英明的老大發現,一個團隊整體能力等於這N個人的戰鬥力的中位數,中位數越高則戰鬥力越強。現在老大想知道,這N個人的團隊整體能力最大爲多少。

【輸入】

輸入文件名爲War.in。
輸入第一行爲三個正整數N,T,M,意義如上述。
後接T行,每行兩個正整數Wi和Vi,代表每個人的戰鬥力值和影響力值。

【輸出】

輸出文件名爲War.out。
輸出一行一個整數,代表這N個人的團隊最大的整體能力。無解輸出-1。

【輸入輸出樣例】

War.in

3 5 70
30 25
50 21
20 20
5 18
35 30

War.out
35

【樣例解釋】

選第2、4、5個人,影響力21+18+30=69<=70,同時該團隊的整體能力爲最高的35。

【數據範圍】

對於30%的數據,保證T200
對於100%的數據,保證T100000N20000 每個人的影響力值100000M2311 .

【題解】

我當時一看這個題目,然後就開始準備打暴力,不怎麼想去想,然後打完暴力30分,再然後就…沒有然後了…
考後據說就是把每個人按照戰鬥力值進行排序,那麼枚舉中位數,對於第 i 個人,我們只需要求出 1i1 中影響力值最小的 n2 個人之和,i+1n 中影響力值最大的 n2 個人之和。
若它們之和再加上當前人的影響力值小於等於 m,就可以更新最大值。
在這之後就得要求區間前K大的數的和了。
只要建一個大根堆,每次比較當前數字與堆頂元素的大小關係,若比堆頂元素大,則不管;若比堆頂元素小,則取出堆頂元素,放入當前數字,然後更新和。
時間複雜度 O(nLogn)

【代碼】

先紀念我的30分暴力代碼

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxx = 200+10;
int n,t,m,ans=0;
bool yes;
int a[maxx],b[maxx],num[maxx];
struct people {
    int c,w;
}ren[maxx];

bool cmp(const people &x,const people &y) {
    return x.c<y.c;
}

void dfs(int k,int sum) {
    int l=t-(n-k);
    for(int i=a[k-1]+1;i<=l;i++)
    if(sum+ren[i].w<=m) {
        a[k]=i;
        b[k]=ren[i].c;
        if(k==n) {
        yes=true;
        if(k&1)
            ans=max(ans,b[(k+1)/2]*2);
        else
            ans=max(ans,b[k/2]+b[k/2+1]);
        }
        else
        dfs(k+1,sum+ren[i].w);
        a[k]=0;b[k]=0;
    }
}

int main() {
    freopen("war.in","r",stdin);
    freopen("war.out","w",stdout);
    scanf("%d%d%d",&n,&t,&m);
    if(t>25)
    return 0;
    for(int i=1;i<=t;i++)
    scanf("%d%d",&ren[i].c,&ren[i].w);
    sort(ren+1,ren+1+t,cmp);
    if(t<=25)
    dfs(1,0);
    if(!yes)
    puts("-1");
    else {
      if(ans&1)
      printf("%.1lf\n",(double)ans/2.0);
      else
      printf("%d\n",ans/2);
    }
    return 0;
}

然後這是AC代碼

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;

inline int read() {
    int in=0,f=1;
    char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())
    if(ch=='-')
        f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())
    in=in*10+ch-'0';
    return in*f;
}

const int size = 100000+10;

struct Node {
    int w,c,num;

    bool operator > (const Node &A) const {
    return A.c<c;
    }
    bool operator < (const Node &A) const {
    return A.c>c;
    }
}node[size];

inline bool cmp(const Node &a,const Node &b) {
    return a.w<b.w;
}

int n,t;
LL m;
bool use[size];

priority_queue<Node ,vector<Node>,greater<Node> > q1;
priority_queue<Node ,vector<Node>,greater<Node> > q2;
priority_queue<Node ,vector<Node>,less<Node> > q3;

int main() {
    freopen("war.in","r",stdin);
    freopen("war.out","w",stdout);
    n=read();t=read();m=read();
    for(int i=1;i<=t;i++)
    node[i].w=read(),node[i].c=read();
    sort(node+1,node+t+1,cmp);

    int k=n>>1;
    LL sum1=0,sum2=0;
    Node Top;
    for(int i=1;i<=t;i++)
    node[i].num=i;
    for(int i=t-k-1;i>=1;i--)
    q1.push(node[i]);
    for(int i=t;i>t-k;i--)
    q2.push(node[i]);
    for(int i=1;i<=k;i++) {
    Top=q1.top();q1.pop();
    sum1+=Top.c;
    use[Top.num]=true;
    }
    for(int i=1;i<=k;i++) {
    Top=q2.top();
    q2.pop();
    sum2+=Top.c;
    q3.push(Top);
    }
    for(int i=t-k;i>k;i--) {
    if(use[i]) {
        sum1-=node[i].c;
        Top=q1.top();
        while(use[Top.num]) {
        q1.pop();
        Top=q1.top();
        }
        sum1+=Top.c;
        use[Top.num]=true;
        q1.pop();
    }
    else use[i]=true;
    if(sum1+sum2+node[i].c<=m) {
        printf("%d\n",node[i].w);
        return 0;
    }
    Top=q3.top();q3.pop();
    sum2-=Top.c;q2.push(Top);
    q2.push(node[i]);
    Top=q2.top();q2.pop();
    sum2+=Top.c;q3.push(Top);
    }
    puts("-1");
    return 0;
}

總結

還得繼續想,dp水平才上的去啊,暴力少打點,多想些吧
最後%%%Yzy大神%%% 即將AK

發佈了39 篇原創文章 · 獲贊 7 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章