POJ - 2411 Mondriaan's Dream(状压dp)
生活随笔
收集整理的這篇文章主要介紹了
POJ - 2411 Mondriaan's Dream(状压dp)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
題目鏈接:點(diǎn)擊查看
題目大意:鋪瓷磚
題目分析:經(jīng)典之經(jīng)典的狀態(tài)壓縮動(dòng)態(tài)規(guī)劃,具體的懶得說了,怠惰ing。。一個(gè)月之前寫的代碼,還好當(dāng)時(shí)注釋做的好,現(xiàn)在稍微一看就能立馬理解,碼到博客上存著吧,三種方法,一種很巧妙很好寫但我至今不知道原理的大牛方法,一種很慢但很好理解的暴力枚舉法,還有一種效率很高不太容易寫的搜索方法,都掛上吧:
暴力枚舉:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=(1<<11)+100;int n,m;bool vis[N];LL dp[15][N];int b[15];bool check(int x) {while(x){if(x&1){x>>=1;if(!(x&1))return false;}x>>=1;}return true; }bool Check(int a,int b)//i,i-1 {int i=0;while(i<m){if((a&(1<<i))==0){if((b&(1<<i))==0)return false;elsei++;}elseif((b&(1<<i))==0)i++;else if(i==m-1||!((a&(1<<(i+1)))&&(b&(1<<(i+1)))))return false;elsei+=2;}return true; }int main() { // freopen("input.txt","r",stdin);for(int i=0;i<15;i++)//預(yù)處理 b[i]=1<<i;while(scanf("%d%d",&n,&m)!=EOF&&n+m){if(n<m)//始終保持m<n,保證高效(因?yàn)闄M著豎著沒什么區(qū)別) swap(n,m);memset(dp,0,sizeof(dp));memset(vis,false,sizeof(vis));for(int i=0;i<b[m];i++)//嘗試第一行可行結(jié)果 {if(check(i)){vis[i]=true;dp[1][i]=1;}}for(int i=2;i<=n;i++)for(int j=0;j<b[m];j++)//第i列 for(int k=0;k<b[m];k++)//第i-1列 {if(Check(j,k))dp[i][j]+=dp[i-1][k];}cout<<dp[n][b[m]-1]<<endl;}return 0; }大牛的剪枝法:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=(1<<11)+100;int n,m;bool vis[N];LL dp[15][N];int b[15];bool check(int x) {while(x){if(x&1){x>>=1;if(!(x&1))return false;}x>>=1;}return true; }int main() { // freopen("input.txt","r",stdin);for(int i=0;i<15;i++)//預(yù)處理 b[i]=1<<i;while(scanf("%d%d",&n,&m)!=EOF&&n+m){if(n<m)//始終保持m<n,保證高效(因?yàn)闄M著豎著沒什么區(qū)別) swap(n,m);memset(dp,0,sizeof(dp));memset(vis,false,sizeof(vis));for(int i=0;i<b[m];i++)//嘗試第一行可行結(jié)果 {if(check(i)){vis[i]=true;dp[1][i]=1;}}for(int i=2;i<=n;i++)for(int j=0;j<b[m];j++)//第i列 for(int k=0;k<b[m];k++)//第i-1列 {if((j|k)!=b[m]-1)continue;if(!vis[j&k])continue;dp[i][j]+=dp[i-1][k];}cout<<dp[n][b[m]-1]<<endl;}return 0; }效率最高的搜索:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=(1<<11)+100;int n,m;bool vis[N];LL dp[15][N];int b[15];bool check(int x) {while(x){if(x&1){x>>=1;if(!(x&1))return false;}x>>=1;}return true; }void dfs(int row,int col,int j,int k)//row:當(dāng)前行數(shù),col:當(dāng)前列數(shù),j:第i-1行狀態(tài),k:第i行狀態(tài) //通過第i行的狀態(tài)搜索第i-1行的狀態(tài) {if(col==m){dp[row][k]+=dp[row-1][j];return;}if(col>m)return;if(k&(1<<col))//若當(dāng)前位置為1 {dfs(row,col+1,j,k);//第col列是個(gè)豎著的,則不放 if(k&(1<<(col+1)))//第col列是個(gè)橫著的,則放 dfs(row,col+2,j|(1<<(col+1))|(1<<col),k);}else//若當(dāng)前位置為0,則上一行橫著豎著都行 dfs(row,col+1,j|(1<<col),k); }int main() { // freopen("input.txt","r",stdin);for(int i=0;i<15;i++)//預(yù)處理 b[i]=1<<i;while(scanf("%d%d",&n,&m)!=EOF&&n+m){if(n<m)//始終保持m<n,保證高效(因?yàn)闄M著豎著沒什么區(qū)別) swap(n,m);memset(dp,0,sizeof(dp));memset(vis,false,sizeof(vis));for(int i=0;i<b[m];i++)//嘗試第一行可行結(jié)果 {if(check(i)){vis[i]=true;dp[1][i]=1;}}for(int i=2;i<=n;i++)for(int j=0;j<b[m];j++)dfs(i,0,0,j);cout<<dp[n][b[m]-1]<<endl;}return 0; }?
超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的POJ - 2411 Mondriaan's Dream(状压dp)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ZOJ - 3777 Problem A
- 下一篇: POJ - 3254 Corn Fiel