狀態壓縮DP題目小節(二)

來源:互聯網
上載者:User

最近做的狀態壓縮DP小節:


http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4257

zoj 4257

一堆氣體相互碰撞產生能量,求最後能產生的最大能量,應該是很基礎的狀態壓縮DP吧,設dp[flag]表示狀態flag時能產生的最大能量,(flag中1表示該氣體還存在,0表示該氣體已經消失)邊界條件是flag所有位都為一時,這時產生的能量為0,然後就枚舉最後剩下的氣體,求最大值即可。

#include <iostream>#include <string.h>#include <stdio.h>#include <algorithm>using namespace std;int dp[1<<10];int bo[11][11];int n;int max(int a,int b){    return a>b?a:b;}int dfs(int flag){    if(dp[flag]!=-1)    return dp[flag];    int i,j;    int ans=0;    for(i=1;i<=n;i++)    {        if(flag&1<<(i-1))        {            for(j=1;j<=n;j++)            {                if(j!=i&&(flag&1<<(j-1))==0)                {                    ans=max(ans,dfs(flag^(1<<(j-1)))+bo[i][j]);                }            }        }    }    return dp[flag]=ans;}int main(){    //freopen("dd.txt","r",stdin);    while(scanf("%d",&n)&&n)    {        int i,j;        for(i=1;i<=n;i++)        {            for(j=1;j<=n;j++)            scanf("%d",&bo[i][j]);        }        memset(dp,-1,sizeof(dp));        int ans=0;        dp[(1<<n)-1]=0;        for(i=0;i<n;i++)        {            ans=max(ans,dfs(1<<i));        }        printf("%d\n",ans);    }    return 0;}

http://acm.hdu.edu.cn/showproblem.php?pid=3001

hdu 3001:

類似於TSP問題,但是這裡不同的是每個點最多可以走兩次,而不是一次,所以我們可以用三進位來表示每一個狀態,我們設dp[now][flag]表示目前在now點狀態為flag時的最小花費,則邊界條件為當(1<<(now-1))==flag時,這時表示起點在now點,還沒開始走,則此時的最小花費是0,還有一個邊界條件為(1<<(now-1))*2==flag,這表示從now點走到now點,這是不可能的,所以把最小花費設為無窮大,剩下的就是狀態轉移了。

dp[now][flag]=dp[pre][flag']+map[pre][now],滿足以下幾個條件:

1:pre!=now且map[pre][now]不為-1(也就是存在邊)

2:flag的第pre位不為0

3:flag'的第now位比flag的第now位少1.

我們求最小值即可,我們要枚舉所有每一位都不為0的狀態,取它們的最小值即為答案。

#include <iostream>#include <string.h>#include <stdio.h>#include <algorithm>#define inf 2100000000using namespace std;int bo[11][11];int dp[11][60000];int bit[60000][11];int pow[12];void init(){    int i,j;    for(i=0;i<=59048;i++)    {        int t=i;        for(j=1;j<=10;j++)        {            bit[i][j]=t%3;            t/=3;        }    }    pow[1]=1;    for(i=2;i<=11;i++)    pow[i]=pow[i-1]*3;}int check(int x){    while(x)    {        if(x%3==0)        return 0;        x/=3;    }    return 1;}int n,m;int min(int a,int b){    return a<b?a:b;}int dfs(int now,int flag){    if(dp[now][flag]!=-1)    return dp[now][flag];    if(pow[now]==flag)    return dp[now][flag]=0;    if(pow[now]*2==flag)    return dp[now][flag]=inf;    int ans=inf,i;    for(i=1;i<=n;i++)    {        if(i!=now&&bit[flag][i]&&bo[i][now]!=-1)        {            int tmp=dfs(i,flag-pow[now]);            if(tmp!=inf)            {                ans=min(ans,tmp+bo[i][now]);            }        }    }    return dp[now][flag]=ans;}int main(){   // freopen("dd.txt","r",stdin);    init();    while(scanf("%d%d",&n,&m)!=EOF)    {        int i,a,b,c;        memset(bo,-1,sizeof(bo));        memset(dp,-1,sizeof(dp));        for(i=0;i<m;i++)        {            scanf("%d%d%d",&a,&b,&c);            if(bo[a][b]==-1||bo[a][b]>c)            bo[a][b]=bo[b][a]=c;        }        int ma=pow[n+1]-1,mi=ma/2;        int ans=inf;        for(i=1;i<=n;i++)        {            for(int j=mi;j<=ma;j++)            {                if(check(j))                ans=min(ans,dfs(i,j));            }        }        if(ans==inf)        ans=-1;       printf("%d\n",ans);    }    return 0;}


http://poj.org/problem?id=3311
poj 3311

也是類似於TSP問題,不過這個時候每個點可以走無限次,所以我們要用依次floyd演算法求一下個點之間的最短路,這裡設dist[u][v]表示u和v之間的最短路,然後就和平常求TSP問題一樣了,我們還是設dp[now][flag]表示當前在now點狀態為flag的最小花費,邊界條件為flag==(1<<(now-1))時,此時表示從起點開始走第一次到達到達now,為其他店還沒開hi走,則dp[now][flag]=dist[0][now],其他情況基本和上體一樣。

注意題目要求最後一定要回到0點。

#include <iostream>#include <string.h>#include <algorithm>#include <stdio.h>#define inf 2100000000using namespace std;int map[11][11];int dp[11][1<<10];int n;int min(int a,int b){    return a<b?a:b;}int dfs(int now,int flag){    //printf("%d %d\n",now, flag);    if(dp[now][flag]!=-1)    return dp[now][flag];    int i,j;    if(flag==(1<<(now-1)))    return dp[now][flag]=map[0][now];    int ans=inf;    if(now==0)    {        for(i=1;i<=n;i++)        {            ans=min(ans,dfs(i,flag)+map[i][now]);        }    }    else    {        for(i=1;i<=n;i++)        {            if(i!=now&&flag&(1<<(i-1)))            {                ans=min(ans,map[i][now]+dfs(i,flag^(1<<(now-1))));            }        }    }    return dp[now][flag]=ans;}void floyd(int n){    int i,j,k;    for(i=0;i<=n;i++)    {        for(j=0;j<=n;j++)        {            for(k=0;k<=n;k++)            {                map[j][k]=min(map[j][k],map[j][i]+map[i][k]);            }        }    }}int main(){    //freopen("dd.txt","r",stdin);    int i,j;    while(scanf("%d",&n)&&n)    {        for(i=0;i<=n;i++)        {            for(j=0;j<=n;j++)            scanf("%d",&map[i][j]);        }        floyd(n);        memset(dp,-1,sizeof(dp));        dp[0][0]=0;        printf("%d\n",dfs(0,(1<<n)-1));    }    return 0;}

poj 2288

還是類似於TSP問題,不過這裡對於花費做了新的定義,花費分為三部分

1:路徑中每個點的權值之和。

2:路徑中相鄰兩個點的權值之積的和。

3:若存在路徑pi->pi+1->pi+2,且pi和pi+2之間有邊,則花費還要加上這三點的權值之和。

我們要求花費最大的路徑以及這樣的路徑的個數

所以我們社狀態時不能只考慮當前點,還要考慮之前的點了,所以我們設dp[now][pre][flag]表示當前點在now,上一個點在pre,此時狀態為flag時的最大花費。設way[now][flag][flag]為dp[now][pre][flag]取最大值時的路徑條數。則接下來我們就和求TSP問題時差不多了,只是求花費時稍微麻煩一點,邊界條件是當now為起點時,即(1<<(now-1))==flag時,此時花費為now點的權值,此時的路徑條數為1.代碼還有一些細節,具體實現請參考代碼。

#include <iostream>#include <string.h>#include <algorithm>#include <stdio.h>#define maxn 100010#define ll long longusing namespace std;ll dp[14][14][1<<13];ll way[14][14][1<<13];int bo[14][14];int v[14];void init(){    memset(bo,0,sizeof(bo));    memset(way,0,sizeof(way));    memset(dp,-1,sizeof(dp));}ll max(ll a,ll b){    return a>b?a:b;}int n;ll dfs(int now,int pre,int flag){   // printf("f");    if(dp[now][pre][flag]!=-1)    return dp[now][pre][flag];    if(1<<(now-1)==flag)    {        way[now][pre][flag]=1;        return dp[now][pre][flag]=v[now];    }    int i,tru=0;    ll ans=0;    for(i=1;i<=n;i++)    {        if(i!=now&&i!=pre&&(flag&(1<<(i-1))))        {            tru=1;            if(bo[i][pre])            {                ll tmp=0;                tmp=dfs(pre,i,flag^(1<<(now-1)));                if(tmp)                {                    tmp+=v[now]+v[now]*v[pre];                    if(bo[i][now])                    tmp+=v[now]*v[pre]*v[i];                    ans=max(ans,tmp);                }            }        }    }    if(!tru)    {        dp[now][pre][flag]=dfs(pre,0,flag^(1<<(now-1)))+v[now]+v[now]*v[pre];        way[now][pre][flag]=1;        return dp[now][pre][flag];    }    if(ans)    {        for(i=1;i<=n;i++)        {            if(i!=now&&i!=pre&&bo[i][pre]&&(flag&(1<<(i-1))))            {                ll tmp=0;                tmp=dfs(pre,i,flag^(1<<(now-1)));                if(tmp)                {                    tmp+=v[now]+v[now]*v[pre];                    if(bo[i][now])                    tmp+=v[now]*v[pre]*v[i];                    if(ans==tmp)                    {                        way[now][pre][flag]+=way[pre][i][flag^(1<<(now-1))];                    }                }            }        }    }    return dp[now][pre][flag]=ans;}int main(){    //freopen("dd.txt","r",stdin);    int ncase;    scanf("%d",&ncase);    while(ncase--)    {        int m,i,a,b,j;        init();        scanf("%d%d",&n,&m);        for(i=1;i<=n;i++)        scanf("%d",&v[i]);        for(i=0;i<m;i++)        {            scanf("%d%d",&a,&b);            bo[a][b]=bo[b][a]=1;        }        ll ans=0,num=0;        if(n==1)        printf("%d 1\n",v[1]);        else        {            for(i=1;i<=n;i++)            {                for(j=1;j<=n;j++)                {                    if(i!=j&&bo[i][j])                    {                        dfs(i,j,(1<<n)-1);                        ans=max(ans,dp[i][j][(1<<n)-1]);                    }                }            }            if(ans==0)            printf("0 0\n");            else            {                for(i=1;i<=n;i++)                {                    for(j=1;j<=n;j++)                    {                        if(i!=j&&bo[i][j]&&dp[i][j][(1<<n)-1]==ans)                        {                            num+=way[i][j][(1<<n)-1];                        }                    }                }                printf("%I64d %I64d\n",ans,num/2);            }        }    }    return 0;}


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.