1. 程式人生 > >7.21 暑假集訓——動態規劃篇(二)

7.21 暑假集訓——動態規劃篇(二)

共17道題目,其中最後五題是拓展題,數位dp和插頭dp沒有寫出來
按照過題的順序記錄, 可以從下往上看~

E-Coins

題意: 告訴你 n 種錢幣的面值和數目,求【1,m】之間,有多少數目是用這些錢幣能恰好湊出的?
思路:經典裸題,參考《挑戰程式設計》P74

注意: 記憶體限制,可以參考書上的利用奇偶性減少記憶體消耗

B-免費餡餅

題意: 現在給這條小徑如圖示上座標:
————————————
0 1 2 3 4 5 6 7 8 9 10
假設餡餅都掉落在0-10這11個位置。gameboy每秒種只有在移動不超過一米的範圍內接住墜落的餡餅。開始時gameboy站在5這個位置,因此在第一秒,他只能接到4,5,6這三個位置中其中一個位置上的餡餅。問gameboy最多可能接到多少個餡餅?(假設他的揹包可以容納無窮多個餡餅)
思路:

定義以時間為序,決策有 站著不動 ,左移和右移兩種,開一個數組記錄在t時刻,i點處掉落的餡餅數

J-Humble Numbers

題意: 只由2,3,5,7相乘得到的數字為Humble Numbers,如1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 25, 27, …
求第n個Humber Numbers
思路: 參考紫皮 第十章
注意: G++和C++ 編譯上的不同

D-How to type

思路: 定義狀態d[i][0]和d[i][1]分別表示第i個字元大寫和小寫所需的最小運算元,遞推一下

A- Big event in HDU ****

思路: 要轉換一下思路,轉換成求0-sum 值中用有n人的m個學院最多湊出多少個數,然後找出這些數中最靠近sum/2的數即可
code:http://paste.ubuntu.com/25197810/

L-How many ways

題意: 這是一個簡單的生存遊戲,你控制一個機器人從一個棋盤的起始點(1,1)走到棋盤的終點(n,m)。遊戲的規則描述如下:
1.機器人一開始在棋盤的起始點並有起始點所標有的能量。
2.機器人只能向右或者向下走,並且每走一步消耗一單位能量。
3.機器人不能在原地停留。
4.當機器人選擇了一條可行路徑後,當他走到這條路徑的終點時,他將只有終點所標記的能量。
我們的問題是機器人有多少種方式從起點走到終點。這可能是一個很大的數,輸出的結果對10000取模。
code:

http://paste.ubuntu.com/25197837/

Beans ****

Doint homework again

樹形dp ****

題意 公司裡上級和下級形成一棵樹,每個人期待參加派對的程度都不同,求在直接上級和下級不同時出現在派對的情況下,最大的期待總值
思路: 先是怎麼儲存樹,我用的vector
然後分別用0,1儲存這個人不參加和參加派對時的最大期待值
code: http://paste.ubuntu.com/25198008/

G&H 求最大相同字元的子矩陣面積 *****

思路: 本來是很不會矩陣求面積的…… 後來居然看著看著 看通暢了……
記錄每一列的最大深度,求出到最左和最右的距離,用這個面積來更新ans
code: http://paste.ubuntu.com/25198040/

因為最後兩題想直接給出程式碼,所以把兩題沒有做出來的放到上面來

數位dp

題意: 求一個區間內滿足條件的數有多少個,條件是該數可以寫成k個b的不同次方的和
參考: 劉聰 《淺談數位類統計問題》

插頭dp HDU P1693

題意: 這裡寫圖片描述

呃…… 略略略

狀壓dp *********

思路: 先看資料範圍 1 ≤ M ≤ 12; 1 ≤ N ≤ 12 可以狀壓沒有錯
然後列舉,d[i][j] 為第i行為j排列時 最大的排列數

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
int pic[15][15];
const int bit=(1<<14)-1;
LL d[15][bit+10];
int n,m;
const int mod=1e8;

bool able(int i,int j){ //判斷第i行 j種排列可行,1.沒有兩個1相鄰,2.沒有荒地種了1
    int lim=0;
    for(int k=0;k<m;k++)
        lim=(lim<<1)+pic[i][k];
    if(lim&j) return false;
    int be=j&1;j>>=1;
    while(j){
        if(be==1&&(j&1)) return false;
        be=j&1; j>>=1;
    }
    return true;
}

bool judge(int i,int j){  //判斷相鄰兩行的i,j排列是否可行,相鄰字元不都為1則可行
    bool res=true;
    for(int k=0;k<m;k++){
        int n=i&1,m=j&1;
        if(n==1&&m==1) res=false;
        i>>=1,j>>=1;
    }
    return res;
}

void print(int j){ //輸出解
    while(j){
        printf("%d",j&1);
        j>>=1;
    }
    cout<<endl;
}


int main()
{
    //freopen("1.txt","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        memset(pic,0,sizeof(pic));
        memset(d,0,sizeof(d));
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++){
            int t;
            scanf("%d",&t);
            pic[i][j]=!t;
        }

        for(int i=0;i<(1<<m);i++)
                if(able(0,i)) d[0][i]=1;

        for(int i=1;i<n;i++){
            for(int j=0;j<(1<<m);j++){
                if(!able(i,j)) {d[i][j]=0;continue;}  //第i行可以取j排列
                for(int k=0;k<(1<<m);k++){
                    if(judge(j,k)&&able(i-1,k)) d[i][j]=(d[i][j]+d[i-1][k])%mod;  //i-1行可以取k排列,i行以取j排列時 可以取的排列數
                }
            }
        }

        LL ans=0;
        for(int j=0;j<(1<<m);j++) ans=(ans+d[n-1][j])%mod;
        printf("%lld\n",ans%mod);
    }
    return 0;
}

區間dp **********

思路: 給出一個多邊形,已知每一個頂點的座標,告訴你每一刀的消耗與座標的關係式 costi, j = |xi + xj| * |yi + yj| % p 。求將這個多邊形分為n-2個三角形所需要的最小消耗

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
struct Point{  //定義點的結構體
    int x,y;
    Point(int x=0,int y=0):x(x),y(y) {};
};
bool operator < (const Point& a,const Point& b){ //兩種排序
    return (a.x!=b.x) ? a.x<b.x: a.y>b.y;
}
bool cmp (const Point &a,const Point &b){
    return (a.x!=b.x) ? a.x>b.x:a.y<b.y;
}
vector<Point> vec;  //點的向量集
const int INF=1e9;
int n,q;
int d[310][310];
int c[310][310];
//已知三角形三個頂點的座標,求面積,利用公式
double area(int i,int j,int k){
    Point a=vec[i],b=vec[j],c=vec[k];
    return (fabs(a.x*(b.y-c.y)+b.x*(c.y-a.y)+c.x*(a.y-b.y))/2.0);
}
//判斷點是不是在已有的多邊形的內部,如果是則為凹多變形,理解
bool judge(int i,int j,int k){
    for(int t=0;t<n;t++){
        if(t==i||t==j||t==k) continue;
        if(area(i,j,k)==area(i,j,t)+area(i,k,t)+area(j,k,t)) return false;
    }
    return true;
}

// 用動態規劃求最優解,重點d[i][j]=min(d[i][j],d[i][k]+d[k][j]+c[i][k]+c[k][j]);
bool solve(){
    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++){
        if(i-j==1||j-i==1) c[i][j]=c[j][i]=0;
        else {
            c[i][j]=c[j][i]=(abs(vec[i].x+vec[j].x)*abs(vec[i].y+vec[j].y))%q;
        }
    }

    for(int i=n-2;i>=0;i--)
        for(int j=i+1;j<n;j++){
            if(j==i+1) d[i][j]=0;
            else{
                d[i][j]=INF;
                for(int k=i+1;k<j;k++){
                    if(!judge(i,j,k)) return false;  //判斷凸邊形
                    d[i][j]=min(d[i][j],d[i][k]+d[k][j]+c[i][k]+c[k][j]);
                }
            }
    }

    return  true;
}
//給點排序,使點按逆時針方向形成一個多邊形
void Sort(){
    sort(vec.begin(),vec.end());
    int lx,ly,rx,ry;
    lx=vec[0].x,ly=vec[0].y;
    rx=vec[n-1].x,ry=vec[n-1].y;
    double k=double (ly-ry)/double (lx-rx);
    double b=double(ly)-k*lx;
    vector<Point> vec2;
    vec2.clear();
    for(int i=0;i<n;i++)
    {
        if(vec[i].x*k-vec[i].y+b<=0){
            vec2.push_back(vec[i]);
            vec[i].x=vec[i].y=-INF;
         }
    }
    sort(vec.begin(),vec.end(),cmp);
    for(int i=0;i<n;i++)
        if(vec[i].x>=lx) vec2.push_back(vec[i]);
    swap(vec,vec2);
}


int main()
{
    //freopen("1.txt","r",stdin);
    while(~scanf("%d%d",&n,&q)){
        vec.clear();
        for(int i=0;i<n;i++){
            int x,y; scanf("%d%d",&x,&y); //讀取每個點
            vec.push_back(Point(x,y));
        }
        Sort();
        memset(d,-1,sizeof(d));

        bool jud=solve();
        if(!jud){
            printf("I can't cut.\n"); continue;
        }
        printf("%d\n",d[0][n-1]);
    }
    return 0;
}