狀壓dp做題記錄(總結用材料)
1.鋪方格(統計方案數)
#include<vector>
#include<map>
#include<ctime>
#define ll long long
using namespace std;
long long dp[12][3000];//最後結果要用long long
bool judge1(int x,int len)//第一行是否合法
{
for(int i=0;i<len;++i)
{
if((x>>i)&1)
{
if(i==len-1)return 0;
if((x>>(i+1))&1)
{
i++;
continue;
}
else return 0;
}
else continue;
}
return 1;
}
bool judge2(int x,int y,int len)//兩行狀態是否能共存
{
for(int i=0;i<len;++i)
{
if((y>>i)&1)//分類討論,要理清分類依據:當前行為1或0,上一行對應為,,,
{ //小細節,判斷某位是否為1,別忘了&1
if(!((x>>i)&1))continue;
else
{
if(i==len-1)return 0;
if((x>>(i+1))&1)
{
if((y>>(i+1))&1)
{
i++;
continue;
}
else return 0;
}
else return 0;
}
}
else
{
if((x>>i)&1)
{
continue;
}
else
{
return 0;
}
}
}
return 1;
}
int main()
{
int h,w;
while(cin>>h>>w&&h+w)
{
memset(dp,0,sizeof(dp));
if(h<w)swap(h,w);
for(int i=0;i<(1<<w);++i)
if(judge1(i,w))dp[1][i]=1;
for(int i=2;i<=h;++i)
{
for(int j=0;j<(1<<w);++j)
{
for(int k=0;k<(1<<w);++k)
{
if(i==2)
{
if(judge2(k,j,w)&&judge1(k,w))dp[i][j]+=dp[i-1][k];
}
else
if(judge2(k,j,w))dp[i][j]+=dp[i-1][k];
}
}
}
long long ans=0;
ans=dp[h][(1<<w)-1];
cout<<ans<<endl;
}
return 0;
}
3.炮兵陣地(三維狀壓+空間複雜度優化)
#include<ctime>
#define ll long long
using namespace std;//為什麼要開三維陣列,因為二維無法表示對應狀態限制下的子解
int a[600];
int num[600];
int sta[110];
long long dp[110][65][65];
int getNum(int x,int n)
{
int ans=0;
for(int i=0;i<n;++i)
{
if((x>>i)&1)ans++;
}
return ans;
}
int init(int n)
{
memset(a,0,sizeof(a));
memset(num,0,sizeof(num));
int tot=0;
for(int i=0;i<(1<<n);++i)
{
if((i&(i>>1))||(i&(i>>2)))continue;
else
{
a[++tot]=i;
num[i]=getNum(i,n);
}
}
return tot;
}
int main()
{
int n,m;
while(cin>>n>>m)
{
memset(sta,0,sizeof(sta));
memset(dp,0,sizeof(dp));
getchar();
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
char ch;
ch=getchar();
if(ch=='P')sta[i]+=(1<<(m-j));
}
if(i!=n)getchar();
sta[i]=~sta[i];
}
int tot=init(m);
long long maxx=0;
for(int i=1;i<=tot;++i)//第一行
{
if(n==1)//只有一行的情況要特判
{
if(!(sta[1]&a[i]))maxx=max(maxx,(long long)getNum(a[i],m));
continue;
}
for(int j=1;j<=tot;++j)//第二行
{
if( !(sta[1]&a[i]) && !(sta[2]&a[j]) && !(a[i]&a[j]) )
{
dp[2][j][i]=num[a[j]]+num[a[i]];
maxx=max(maxx,dp[2][j][i]);
}
}
}
for(int i=3;i<=n;++i)
{
for(int j=1;j<=tot;++j)//當前
{
for(int k=1;k<=tot;++k)//上一
{
for(int l=1;l<=tot;++l)//上二
{
if( !(a[j]&sta[i])&& !(a[k]&sta[i-1])&&!(a[l]&sta[i-2]) &&!(a[j]&a[k]) &&!(a[j]&a[l]) &&!(a[k]&a[l]) )
{
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+num[a[j]]);
maxx=max(maxx,dp[i][j][k]);
}
}
}
}
}
cout<<maxx<<endl;
}
return 0;
}
4.(TSP弗洛伊德+狀壓)
using namespace std;
const ll INF=999999999;
int f[12][12];
void floyed(int n)
{
for(int k=0;k<=n;++k)//弗洛伊德從0/1開始的差別:有無0點
{
for(int i=0;i<=n;++i)
{
for(int j=0;j<=n;++j)
{
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);//直接在構建的圖上更新
}
}
}
}
int dp[1500][12];
int main()
{
int n;
while(cin>>n&&n)
{
memset(f,0,sizeof(f));
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
cin>>f[i][j];
floyed(n);
for(int i=0;i<(1<<n);++i)
for(int j=0;j<=n;++j)dp[i][j]=INF;
for(int i=0;i<n;++i)dp[1<<i][i+1]=f[0][i+1];//處理邊界
for(int i=1;i<(1<<n);++i)//error:狀態描述中此狀態不包含j
{
for(int j=1;j<=n;++j)
{
if(i&(1<<(j-1)))
{
for(int k=1;k<=n;++k)
{
if((i-(1<<(j-1)))&(1<<(k-1)))
dp[i][j]=min(dp[i][j],dp[i-(1<<(j-1))][k]+f[k][j]);
}
}
}
}
int ans=INF;
for(int i=1;i<=n;++i)
{
ans=min(ans,dp[(1<<n)-1][i]+f[i][0]);
}
cout<<ans<<endl;
}
return 0;
}
錯題思路(或者未實現的思路):
#define ll long long
using namespace std;
const ll INF=999999999;
int f[12][12];
void floyed(int n)
{
for(int k=1;k<=n;++k)
{
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
}
}
}
int dp[1500][12];
int main()
{
int n;
while(cin>>n)
{
memset(f,0,sizeof(f));
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
cin>>f[i][j];
floyed(n);
for(int i=0;i<(1<<n);++i)
for(int j=0;j<=n;++j)dp[i][j]=INF;
for(int i=0;i<=n;++i)dp[0][i]=f[0][i];
for(int i=0;i<(1<<n);++i)
{
for(int j=1;j<=n;++j)
{
if(dp[i][j])
{
for(int k=1;k<=n;++k)
{
if((i+(1<<(j-1)))&&(1<<(k-1)))continue;
else
dp[i+(1<<(j-1))][k]=min(dp[i+(1<<(j-1))][k],dp[i][j]+f[j][k])
}
}
}
}
int ans=INF;
for(int i=1;i<=n;++i)
{
ans=min(ans,dp[((1<<n)-1)-(1<<(i-1))][i]+f[i][0]);
}
cout<<ans<<endl;
}
return 0;
}
修改正確後:
#include <iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll INF=999999999;
int f[12][12];
void floyed(int n)
{
for(int k=0;k<=n;++k)
{
for(int i=0;i<=n;++i)
{
for(int j=0;j<=n;++j)
{
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
}
}
}
int dp[1500][12];
int main()
{
int n;
while(cin>>n&&n)
{
memset(f,0,sizeof(f));
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
cin>>f[i][j];
floyed(n);
for(int i=0;i<(1<<n);++i)
for(int j=0;j<=n;++j)dp[i][j]=INF;
for(int i=0;i<n;++i)dp[1<<i][i+1]=f[0][i+1];
for(int i=0;i<(1<<n);++i)
{
for(int j=1;j<=n;++j)
{
if(dp[i][j]&&(i&(1<<(j-1))))
{
for(int k=1;k<=n;++k)
{
if(i&(1<<(k-1)))continue;
else
dp[i+(1<<(k-1))][k]=min(dp[i+(1<<(k-1))][k],dp[i][j]+f[j][k]);
}
}
}
}
int ans=INF;
for(int i=1;i<=n;++i)
{
ans=min(ans,dp[((1<<n)-1)][i]+f[i][0]);
}
cout<<ans<<endl;
}
return 0;
}
5.順路A,概率類題目(注意精度)
題解地址:https://blog.csdn.net/zhenlingcn/article/details/76021104?locationNum=5&fps=1
#include<ctime>
#define ll long long
using namespace std;
double a[50];
int main()
{
int n;
int cas=0;
while(cin>>n&&n)
{
char ch[110];
for(int i=1;i<=n;++i)
{
scanf("%s",&ch);
a[i]=atof(ch);
}
int num=0;
for(int i=0;i<strlen(ch);++i)
{
if(ch[i]=='.')
{
num=strlen(ch)-1-i;
break;
}
}
int ans=0;
for(int i=1;i<=10000;++i)
{
int minn=0,maxx=0;
for(int j=1;j<=n;++j)
{
minn+=ceil( 1.0*i*(a[j]*pow(10,num)-0.5000)/100/pow(10,num) );//精度
maxx+=floor( 1.0*i*(a[j]*pow(10,num)+0.4999)/100/pow(10,num) );
}
if(i>=minn&&i<=maxx)
{
ans=i;
break;
}
}
if(ans)cout<<"Case "<<++cas<<": "<<ans<<endl;
else cout<<"Case "<<++cas<<": "<<"error"<<endl;
}
return 0;
}
6.哈密爾頓路徑求最大權值:
/*
Wa:n==1沒特判、陣列開的太大超記憶體限制(1<<14,20,20)、減法狀態轉移陣列下溢;
PS:這題的結果處理上也是個亮點;
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int v[20];
int g[20][20];
int dp[9000][15][15];
ll num[9000][15][15];
bool judge(int i,int j,int k,int l)
{
if(l==j||j==k||k==l)return 0;
if(!g[j][k]||!g[k][l])return 0;
if( !((i&(1<<(j-1)))&&(i&(1<<(k-1)))&&(i&(1<<(l-1)))) )return 0;
if(dp[i-(1<<(l-1))][j][k]==-1)return 0;//減法運算注意陣列下溢;
return 1;
}
int main()
{
/*區別上面弗洛依德演算法+狀壓dp解決旅行商問題:
分析:
*狀壓dp與圖聯絡,一般情況下用二維【sta】【停駐點】陣列,但因為 所求的路徑權值有三個來源:
點的權值相加、邊的權值相加(端點的乘積)、額外三角形權值,為求得第三個權值來源,
需要記錄下2個點的狀態,因此,開三維陣列,【sta】【停駐點上一點】【停駐點】。
時間複雜度:
o(1024*8* 13*13*13==2e7);
空間複雜度:
16384*20*20==6e6,完全ok。
處理步驟:
*n點數,m邊數,v[]存點權值, g[][]構圖(無向圖),dp存最大權值,num[]存路徑數;
*變數初始化:因為求最大權值,陣列均初始化為0即可;
*邊界處理:三維一般用兩層迴圈初始化邊界,本題中即含兩點的狀態:權值即點權值和+邊權值和,
路徑數目即初始化為1。
*狀態轉移:dp【sta+k點】【j】【k】=max dp【sta】【i】【j】+v【k】+v【k】*v【j】+(三角形判斷)
上式執行的條件:sta中有i點、j點,無k點;i與j相連,j與k相連,dp【】【i】【j】
已經賦值(沒賦值的即該狀態不滿足所有為1的點全被訪問過),(保證由已知推算未知);
**另外附加判斷三角形,,,(略)
**若新值>原來值,更新dp,同時num更新為新的哈密爾頓路徑數
**若新值==原來值,原來的哈密路徑數+新的哈密路徑數
*答案選擇,dp【二的n次方-1】【i隨機】【j隨機】中取max作為 哈密路徑最大權值,同時對應
num【】【】【】為路徑數,又因為題目要求 一條路徑的反向與正向味同一條路,所以所取路徑數/2作為
最終答案。
*輸出哈密爾頓路徑最大權值,哈密爾頓路徑數;
*/
int t;
cin>>t;
while(t--)
{
memset(g,0,sizeof(g));
memset(v,0,sizeof(v));
memset(dp,-1,sizeof(dp));
memset(num,0,sizeof(num));
int n,m;
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>v[i];
if(n==1)//對於1的情況要特判
{
cout<<v[1]<<" 1"<<endl;
continue;
}
for(int i=1;i<=m;++i)
{
int x,y;
cin>>x>>y;
g[x][y]=g[y][x]=1;
}
for(int j=1;j<=n;++j)
for(int k=1;k<=n;++k)
{
if(g[j][k])
{
int sta=(1<<(j-1))+(1<<(k-1));
dp[sta][j][k]=v[j]+v[k]+v[j]*v[k];
num[sta][j][k]=1;
}
}
for(int i=0;i<(1<<n);++i)
{
for(int j=1;j<=n;++j)
{
for(int k=1;k<=n;++k)
{
for(int l=1;l<=n;++l)
{
if(judge(i,j,k,l))
{
int temp=dp[i-(1<<(l-1))][j][k]+v[l]+v[l]*v[k];
if(g[j][l])temp+=v[j]*v[k]*v[l];
if(temp>dp[i][k][l])
{
dp[i][k][l]=temp;
num[i][k][l]=num[i-(1<<(l-1))][j][k];
}
else if(temp==dp[i][k][l])
{
num[i][k][l]+=num[i-(1<<(l-1))][j][k];
}
}
}
}
}
}
ll ans=0,res=0;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
if(dp[(1<<n)-1][i][j]>ans)
{
ans=dp[(1<<n)-1][i][j];
res=num[(1<<n)-1][i][j];
}
else if(ans==dp[(1<<n)-1][i][j])
{
res+=num[(1<<n)-1][i][j];
}
}
}
cout<<ans<<" "<<res/2<<endl;
}
return 0;
}
7.旅行團參觀景點,難點權值的預處理(暴力列舉法)
#include <cstdio>
#include <cmath>
#include <cstring>
#define ll long long
using namespace std;
const ll INF=999999999;
int like[15][15];
int cost[15];
int cp[15][15];
int dp[1500][15];
int v[1500][15];
void init(int n,int m)
{
memset(v,0,sizeof(v));
for(int i=1;i<=m;++i)
{
for(int j=0;j<(1<<n);++j)
{
for(int k=1;k<=n;++k)
{
if(j&(1<<(k-1)))//滿足這個if才能繼續下面的語句,WA:下面那個for放在了if外面;
{
v[j][i]+=like[k][i]-cost[i];
for(int l=k+1;l<=n;++l)
{
if(j&(1<<(l-1)))v[j][i]+=cp[k][l];
}
}
}
}
}
}
void DP(int n,int m)
{
for(int i=0;i<(1<<n);++i)
for(int j=1;j<=m;++j)dp[i][j]=-INF;//有負解,所以全部初始化為極小值,不能是0;
for(int i=0;i<(1<<n);++i)dp[i][1]=v[i][1];
for(int i=2;i<=m;++i)
{
for(int j=0;j<(1<<n);++j)
{
for(int k=j;k<(1<<n);++k)
{
if((j&k)==j)
dp[j][i]=max(dp[j][i],dp[k][i-1]+v[j][i]);
}
}
}
}
int main()
{
/*
問題分析:
n個遊客,m個景點,每個遊客對不同景點有不同的喜愛度like[1~n][1~m],不同景點不通花費cost[1~m],n個人中某兩個人在一起時會產生附加權值cp【1~n】【1~n】(正的)。遊客們按順序依次參觀景點,參觀過程可刪掉某一遊客且不能再次加入,問怎樣安排 能使最後的權值最大,最大為多少。
狀態dp【sta】【當前景點】;
轉移方程:dp【sta1】【當前景點】=max{dp【sta包含於,,,,,,sta2】【上一景點】+v【sta】【當期景點】}
步驟描述:
*陣列初始化 :求最大值,初始化為0即可,dp也可初始化為-1,具體看情況。
*輸入:n,m,like(n,m),cost(m),cp,
*邊界處理:dp【all sta】【1】=v【sta】【1】;
**v【】【】的預處理:
迴圈:景點、狀態、數位/人員1、數位2; o(10*1024*10*10)
*狀態轉移:迴圈:景點、狀態1、狀態2,轉移條件:狀態1含於狀態二;
o(10*1024*1024);
*答案處理,dp【各種sta】【m】中選最大值,據題意輸出答案。
*/
int n,m;
while(cin>>n>>m&&n+m)
{
memset(like,0,sizeof(like));
memset(cost,0,sizeof(cost));
memset(cp,0,sizeof(cp));
for(int i=1;i<=m;++i)cin>>cost[i];
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)cin>>like[i][j];
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)cin>>cp[i][j];
init(n,m);
DP(n,m);
int ans=-1;
for(int i=0;i<(1<<n);++i)ans=max(ans,dp[i][m]);
if(ans>0)cout<<ans<<endl;
else cout<<"STAY HOME"<<endl;
}
return 0;
}
8.二傻補作業,一定補不完(亮點:輸出方案路徑),,,
#include <cstdio>
#include <cmath>
#include <cstring>
#define ll long long
using namespace std;
const int INF=999999999;
string name[20];
int limit[20],cost[20];
int day[40000];
int dp[40000];
int memor[40000];
void init(int n)
{
memset(day,0,sizeof(day));
for(int i=0;i<(1<<n);++i)
{
int sum=0;
for(int j=0;j<n;++j)
{
if(i&(1<<j))
{
sum+=cost[j+1];
}
}
day[i]=sum;
}
}
void print(int sta)
{
if(sta==0)return ;
print(sta-(1<<(memor[sta]-1)));
cout<<name[memor[sta]]<<endl;
}
int main()
{
/*
分析:
dp【sta】表示在sta狀態(已做完的科目用1表示)下扣除的分數;
狀態轉移方程:dp【sta】=min{dp【sta-k】+(sta-k)代表的天數+k所需要的天數-k的限制時間};
其中 【狀態】對應的天數可以暴力列舉預處理 day【sta】,迴圈:狀態、數位,o(32768*15)複雜度不超時限。
處理步驟:
*資料輸入:n科目數、string【1~n】科目名、limit【1~n】科目時限、cost【1~n】科目花費時間、time【sta】預處理不同狀態所對應的天數;
*狀態轉移:
迴圈:狀態、科目;
轉移條件:狀態中含有該科目;轉移過程同時記錄 狀態最優值對應的最後一門科目meor【sta】=k;
注意:減法注意陣列下溢;
*輸出最優解:即目標狀態dp【sta】,方案遞迴輸出;
*/
int t;
cin>>t;
while(t--)
{
memset(limit,0,sizeof(limit));
memset(cost,0,sizeof(cost));
int n;
cin>>n;
for(int i=1;i<=n;++i)cin>>name[i]>>limit[i]>>cost[i];
init(n);
memset(memor,0,sizeof(memor));
for(int i=1;i<(1<<n);++i)
{
dp[i]=INF;
for(int j=n;j>=1;--j)//正向WA,這裡逆向考慮,因為小狀態到大狀態轉移時不是每次從最小的選起,例如選最後一門科目時,應該最大序的科目,而按正向考慮,選擇的是最小序的科目。
{
if(i&(1<<(j-1)))
{
int temp=day[i-(1<<(j-1))]+cost[j]-limit[j];
if(temp<0)temp=0;
if(dp[i-(1<<(j-1))]+temp<dp[i])
{
dp[i]=dp[i-(1<<(j-1))]+temp;
memor[i]=j;
}
}
}
}
cout<<dp[(1<<n)-1]<<endl;
print((1<<n)-1);
}
return 0;
}
9.方格取數(這麼高的複雜度能水過?)
沒啥說的就這麼幾步:
/*
*輸入資料
*縮減可行狀態數,(不然陣列會爆,時間也會爆)
*打表預處理權值
*狀態轉移
*選擇最優解輸出資料
*/
#include <cstdio>
#include <cmath>
#define ll long long
using namespace std;
const int INF=999999999;
int g[25][25];
ll dp[20000][25];
ll sta[20000];
ll v[20000][25];
int pre(int n)//縮減可行解&&預處理權值打表
{
memset(sta,0,sizeof(sta));
memset(v,0,sizeof(v));
int tot=0;
for(int i=0;i<(1<<n);++i)
{
if(i&(i>>1)){}
else sta[++tot]=i;
}
for(int k=1;k<=n;++k)//權值打表;
for(int i=1;i<=tot;++i)
{
int sum=0;
for(int j=0;j<n;++j)
{
if(sta[i]&(1<<j))sum+=g[k][j+1];
}
v[i][k]=sum;
}
return tot;//max<20000
}
void init(int n)
{
memset(g,0,sizeof(g));
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)cin>>g[i][j];
}
void solve(int n,int tot)
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=tot;++i)dp[i][1]=v[i][1];
for(int i=2;i<=n;++i)
{
for(int j=1;j<=tot;++j)
{
for(int k=1;k<=tot;++k)
{
if(!(sta[j]&sta[k]))
{
dp[j][i]=max(dp[j][i],dp[k][i-1]+v[j][i]);
}
}
}
}
}
int main()
{
int n;
while(cin>>n)
{
init(n);
int tot=pre(n);
solve(n,tot);
ll ans=0;
for(int i=1;i<=tot;++i)ans=max(ans,dp[i][n]);
cout<<ans<<endl;
}
}
10.刪除迴文串最少次數(玄學,dp過程子集合正向列舉不行,for不做小技巧不行)
#include <cstdio>
#include <cmath>
#include <cstring>
#define ll long long
using namespace std;
const int INF=999999999;
bool mark[(1<<16)+100];
int dp[70000];
char ch[20];
void pre(string str)//迴文串判斷預處理;
{
memset(mark,0,sizeof(mark));
int n=str.length();
for(int i=0;i<(1<<n);++i)
{
int tot=0;
for(int j=0;j<n;++j)
{
if(i&(1<<j))ch[++tot]=str[j];
}
bool flag=1;
for(int j=1;j<=tot/2;++j)
if(ch[j]!=ch[tot-j+1])flag=0;
mark[i]=flag?1:0;
}
}
void DP(string str)//DP過程不是很好處理,也可能是感冒,,,
{
int n=str.length();
for(int i=0;i<(1<<n);++i)dp[i]=INF;
dp[0]=0;
/*for(int i=1;i<(1<<n);++i)//母狀態
{
for(int j=1;j<=i;(++j)&=i)//刪去的子狀態
{
if(mark[j])
dp[i]=min(dp[i],dp[i-j]+1);
}
}*/
for(int i=1;i<(1<<n);i++)
{
for(int j=i;j>0;(--j)&=i)//列舉j狀態下的子集
{
if(mark[j])//如果該子集合法則列入方程計算
{
dp[i]=min(dp[i],dp[i-j]+1);
}
}
}
}
int main()
{
/*
分析:容易想到方程dp【sta-子sta】=min{ dp【sta-子狀態】+1 };
條件:被減的子狀態應當是迴文串;
*/
int t;
cin>>t;
while(t--)
{
string str;
cin>>str;
pre(str);
DP(str);
cout<<dp[(1<<str.length())-1]<<endl;
}
return 0;
}
11.哈密爾頓迴路(選定首尾點、計數問題一般用long long);
題意:給n個珠子,以及構圖規則(無向圖),求其中哈密爾頓迴路的數目,正向與反向不一樣。
#define ll long long
using namespace std;
ll dp[270000][20];
int g[20][20];
int main()
{
int n,m;
while(cin>>n>>m)
{
memset(dp,0,sizeof(dp));
memset(g,0,sizeof(g));
for(int i=1;i<=m;++i)
{
int x,y;
cin>>x>>y;
g[x][y]=g[y][x]=1;
}
//for(int i=1;i<=n;++i)dp[1<<(i-1)][i]=1;//WA
dp[1][1]=1;//設定環的規則,從一開始到一結束,因此其他單個珠子2,3...在起點時無法形成環項鍊,因此初值為0
for(int i=2;i<(1<<n);++i)
{
for(int j=1;j<=n;++j)//當前
{
for(int k=1;k<=n;++k)//上一個
{
if((i&(1<<(j-1)))&&(i&(1<<(k-1)))&&g[j][k])//沒加dp!=-1的原因,無其他附加權值,+0對結果無影響,(區別哈密爾頓路徑那個地方);
{
dp[i][j]+=dp[i-(1<<(j-1))][k];
}
}
}
}
ll ans=0;
for(int i=1;i<=n;++i)//上文邊界處理呼應
{
if(g[1][i])ans+=dp[(1<<n)-1][i];
}
cout<<ans<<endl;
}
return 0;
}
13.(連連看,難想,狀態上限和n混淆,思路較新穎)
#include <cstdio>
#include <cmath>
#include <cstring>
#define ll long long
using namespace std;
int dp[2050][1500];
int a[1500];
int main()
{
/*
思路:dp不太好想,這裡考慮每個點可操作的其他點為該點往下的9個點,為什麼不是5個點呢,因為該點上面的點至多可操作該點下的第1、2、3、4個點;
所以,狀態可以用10位二進位制表示,因此dp【sta】【i】表示當前判斷以i為首狀態為sta時第i個數是否可消除,狀態轉移關係dp【sta】【i】=(i+1~i+9逐個判斷權值是否相同)
並且距離小於等於“邏輯6”,若可消除,生成新的狀態dp【new sta】【i+1】。
處理步驟:
*輸入:n存資料個數,a【1~n】存元素,dp【1<<10】【1000】,初始化0,表示狀態不存在;
*邊界處理:i=1時,只有“實際距離”6之內的可能被消除,按條件處理狀態。
*狀態轉移:從第二層開始轉移,轉移條件:這一層有這一狀態.
*目標狀態:dp【0】【n+1】是否存在,原因:若序列可以完全消除,到最後一個數據時,其位元位必定為1,否則不可消除。進而轉移
到dp【0】【n+1】=1.
*輸出答案
*/
int n;
while(cin>>n)
{
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
for(int i=n;i>=1;--i)cin>>a[i];
for(int i=1