2017 暑假艾教集訓 day2

練習題 :https://cn.vjudge.net/contest/175510#overview
CTSC 拯救大兵瑞恩
做法:狀壓BFS,每個節點加一個當前所有的鑰匙數,然後進行普通的BFS即可


POJ 2411
做法:輪廓線 狀壓經典題,先DFS出 s 狀態和它的父親 pre ,每次轉移 dp[i][s]+=dp[i-1][pre];
DFS時 每次有橫着放和豎着放, 規定橫着放全爲1,而豎着放上面爲1 。 其餘爲0.
優化: DFS時 m  越大,深度越高所以 m 爲min (n,m)
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;

int n,m;
int cnt;
int path[3000][2];
int dp[11][(1<<5)];
void dfs(int pos,int now,int pre)
{
   if(pos>m) return;
   if(pos==m)
   {
       path[cnt][0]=now;
       path[cnt++][1]=pre;
   }
   dfs(pos+2,now<<2|3,pre<<2|3);
   dfs(pos+1,now<<1|1,pre<<1);
   dfs(pos+1,now<<1,pre<<1|1);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0) break;
        if(n%2==1 && m%2==1)
        {
            printf("0\n");
            continue;
        }
        if(m>n) swap(n,m);

        cnt=0;
        dfs(0,0,0);

        memset(dp,0,sizeof(dp));
        dp[0][(1<<m)-1]=1;

        for(int i=1;i<=n;++i)
        {
            for(int j=0;j<cnt;++j)
            {
                dp[i][path[j][0]]+=dp[i-1][path[j][1]];
            }
        }
        printf("%d\n",dp[n][(1<<m)-1]);
    }
    return 0;
}


HDU 1565
做法:狀壓DP,如果直接存(1<<20)或者直接枚舉的話 時間和空間浪費很大。  所以我們先用 i&(i<<1) || i&(i>>1) 選取行的可選狀態, 會大大降低複雜度。 然後每次轉移的時候 判斷一下 way[k] & way[j] 即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll g[25][25];
int n;
ll ans;
ll dp[21][6666];
ll way[6666];
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<n;++j)
            {
                scanf("%I64d",&g[i][j]);
            }
        }

        memset(dp,0,sizeof(dp));
        int cnt=0;
        ans=0;
        for(int i=0;i<(1<<n);++i)
        {
            if(i&(i<<1) || i&(i>>1)) continue;
            way[cnt]=i;

            for(int j=0;j<n;++j)
            {
                if((i>>j)&1) dp[0][cnt] += g[0][j];
            }
            ans=max(ans,dp[0][cnt]);
            cnt++;
        }

        for(int i=1;i<n;++i)
        {
            for(int j=0;j<cnt;++j)
            {
                ll ma=0;
                for(int k=0;k<cnt;++k)
                {
                    if(way[k] & way[j]) continue;
                    ma=max(ma,dp[i-1][k]);
                }
                for(int k=0;k<n;++k)
                {
                   if((way[j]>>k)&1)
                   {
                      ma+=g[i][k];
                   }
                }
                dp[i][j]=ma;
                ans=max(ans,ma);
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


HDU 2167
做法:和上一題一樣,只是在枚舉上一行的時候多了兩種狀態(因爲選了中間點,周圍八個點都不可選)


CF 757D
做法:dp[I][J],I是位數,J是狀態數(劃出過的數) ,每次向後枚舉做dp即可
優化:因爲一共有八十個數字(2進制)且答案要連續,所以 最大連續數位爲20 (重要減枝)!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1<<20;
int a[105];
int dp[105][maxn+5];
int main()
{
    int n;
    scanf("%d",&n);

    for(int i=0;i<n;++i)
    {
        char c;
        scanf(" %c",&c);
        a[i] = c-'0';
    }

    for(int i=0;i<n;++i)
    {
        dp[i][0]=1;
        for(int j=0;j<(1<<20);++j)
        {
            if(!dp[i][j]) continue;
            int temp=0;
            for(int k=i;k<n;++k)
            {
                temp=(temp<<1)|a[k];
                if(!temp) continue;
                if(temp > 20) break;
                dp[k+1][j|(1<<(temp-1))] = (dp[k+1][j|(1<<(temp-1))]+ dp[i][j])%mod;
            }
        }
    }
    int ans=0;
    for(int i=0;i<=n;++i)
    {
        for(int j=1;j<=20;++j)
        {
            ans = (ans + dp[i][(1<<j)-1])%mod;
        }
    }
    printf("%d\n",ans);
    return 0;
}



HDU 5816
做法:狀壓DP ,dp[S],S爲當前狀態(n+m位二進制), 其前m個爲 B牌的情況,後n個爲A盤的情況
1.當傷害大於HP時,沒必要接着往下做
2.當A-B+1<=0 沒有手牌結束(免費有一張,每張A相當於加一張牌,每張B消耗一張牌)
3.轉移的時候就O(n+m)轉移
4.算上分子時 ,爲 sum (dp[S] *(n+m-a-b)!) ,S爲傷害大於hp的情況,剩餘的牌可以隨便抽(全排列)。
5.算下分母時,就是所有牌全排列即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int harm[22];
ll fat[22];
ll dp[1<<21];
int n,m,hp,N;
int main()
{
    fat[0]=1;
    for(int i=1;i<22;++i) fat[i] = i * fat[i-1];
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&hp,&n,&m);
        N=n+m;
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        for(int i=0;i<m;++i) scanf("%d",&harm[i]);
        for(int i=0;i<(1<<N);++i)
        {
            if(dp[i]==0) continue;
            int na=0,nb=0,h=0;

            for(int j=0;j<m;++j)
            {
                if( i&(1<<j))
                {
                    nb++;  h+=harm[j];
                }
            }
            if(h>=hp) continue;
            for(int j=m;j<N;++j)
            {
                if(i&(1<<j)) na++;
            }
            if(na-nb+1<=0) continue;

            for(int j=0;j<N;++j)
            {
                if(i&(1<<j)) continue;
                dp[i|(1<<j)] += dp[i];
            }
        }
        ll up=0 ,down=fat[N];
        for(int i=0;i<(1<<N);++i)
        {
            if(dp[i]==0) continue;

            int na=0 , nb=0 ,h=0;
            for(int j=0;j<m;++j)
            {
                if( i &(1<<j))  h+=harm[j] ,++na;
            }
            for(int j=m;j<(n+m);++j)
            {
                if(i &(1<<j)) ++nb;
            }
            if(h>=hp) up+= fat[N-na-nb] * dp[i];
        }
        ll g=__gcd(up,down);
        printf("%I64d/%I64d\n",up/g, down/g);
    }
    return 0;
}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章