发现dp调试打最后二维dp表非常有用
1.吃奶酪类
先出状态,再走到哪
dp[1][0]=0;
for(int i=3;i<=maxn;i++){//状态
for(int j=1;j<=n;j++){//走过j
if(i&(1<<j)){
for(int k=0;k<=n;k++){//刚才在k
dp[i][j]=;
}
}
}
}
P1433 吃奶酪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<iostream>
#include<math.h>
using namespace std;
int n;
double INF=1000000000;
int maxn;//maxn不能在这里=
double a[21][2];
double dp[1024*1024+1][16];
double dis[16][16];
void solu(){
dp[1][0]=0;
for(int i=3;i<=maxn;i++){//0必然走过,>=3//状态
for(int j=1;j<=n;j++){//走到哪个
if(i&(1<<j)){
for(int k=0;k<=n;k++){//走过上一个奶酪后,状态为i-(1<<j)
if((i-(1<<j))&(1<<k)){
dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+dis[k][j]);
}
}
}
}
}
double ans=INF;
for(int i=1;i<=n;i++)ans=min(ans,dp[maxn][i]);
printf("%.2lf",ans);
}
int main(){
cin>>n;
maxn=(1<<(n+1))-1;
a[0][0]=a[0][1]=0;
for(int i=1;i<=n;i++){
cin>>a[i][0]>>a[i][1];
}
for(int i=0;i<=n;i++){
for(int j=i+1;j<=n;j++){
dis[i][j]=sqrt((a[i][0]-a[j][0])*(a[i][0]-a[j][0])+(a[i][1]-a[j][1])*(a[i][1]-a[j][1]));
dis[j][i]=dis[i][j];
}
dis[i][i]=0;
}
for(int i=0;i<=maxn;i++){
for(int j=0;j<=n;j++){
dp[i][j]=INF;
}
}
solu();
}
91. 最短Hamilton路径 - AcWing题库
n-1为终点
#include<iostream>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
int n;
ll maxn;
ll dis[21][21];
ll dp[1024*1024+1][21];
void solu(){
dp[1][0]=0;
//i==3&&j==1真是个依赖项
for(ll i=3;i<=maxn;i++){
for(int j=1;j<=n-1;j++){
if(i&(1<<j)){
for(int k=0;k<=n-1;k++){
if((i-(1<<j))&(1<<k)){
dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+dis[k][j]);
}
}
}
}
}
cout<<dp[maxn][n-1];
}
int main(){
cin>>n;
maxn=(1<<n)-1;
for(int i=0;i<=n-1;i++){
for(int j=0;j<=n-1;j++)cin>>dis[i][j];
}
for(ll i=0;i<=maxn;i++)
for(int j=0;j<=n-1;j++){
dp[i][j]=INF;
}
solu();
}
2.棋盘放置类
一行一行逐渐生成,
任何需要积累的,就记住生成头,和状态转化
for(int j=0;j<maxm;j++)//建议生成第一行,之后i=2开始遍历
if(行内合法)dp[1][j]=1;
for(int i=2;i<=n;i++)//枚举n行
for(int j=0;j<maxm;j++)//当前行状态
if(行内合法)
for(int k=0;k<maxm;k++)
if(行内合法&&行间合法)
dp[i][j]+=dp[i-1][k];
2.1Corn Fields G
顺带,可以先预处理行内有效,因为每一行都用
状态枚举时for(int j=0;j<行内合法状态数;j++)
题目描述
Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12, 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.
Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.
农场主 JohnJohn 新买了一块长方形的新牧场,这块牧场被划分成 M 行 N 列(1≤M≤12,1≤N≤12),每一格都是一块正方形的土地。 JohnJohn 打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是 JohnJohn 不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
JohnJohn 想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
输出格式
一个整数,即牧场分配总方案数除以 100,000,000100,000,000 的余数。
#include<iostream>
#define ll long long
using namespace std;
#define mod 100000000
ll n,m;ll maxm;
ll board[13];
ll dp[13][(1<<12)+1];
void solu(){
//像链表头一样,我们是单独处理的
for(ll j=0;j<maxm;j++){
if(j&(j<<1))continue;
if(!((j&board[1])==j))continue;
dp[1][j]=1;
}
for(ll i=2;i<=n;i++)
for(ll j=0;j<maxm;j++){
if(j&(j<<1))continue;
if(!((j&board[i])==j))continue;//j要是棋盘的子集
for(ll k=0;k<maxm;k++){
if((k&(k<<1))||(k&j))continue;
if(!((k&board[i-1])==k))continue;
dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
}
}
ll ans=0;
for(ll j=0;j<maxm;j++)ans=(ans+dp[n][j])%mod;
cout<<ans;
}
int main(){
cin>>n>>m;
maxm=1<<m;
for(ll i=1;i<=n;i++){//每一行压缩成数
for(ll j=0;j<m;j++){
ll temp;cin>>temp;
board[i]+=temp<<j;
}
}
solu();
}
2.2互不侵犯
P1896 [SCOI2005] 互不侵犯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这里有要求放多少个,加上一维
生成第一行时,只有[num[j]]才有效
注意开到dp[10][1<<m][k<=n*n]
#include<iostream>
#define ll long long
using namespace std;
ll n,s;
ll maxn;
ll dp[10][1025][101];
ll num[1025];
void solu(){
for(ll j=0;j<maxn;j++)
dp[1][j][num[j]]=1;
for(ll i=2;i<=n;i++){
for(ll j=0;j<maxn;j++){
if(j&(j<<1))continue;
for(ll k=0;k<maxn;k++){
if(k&(k<<1))continue;
if((k&j)||(k&(j<<1))||(j&(k<<1)))continue;
for(ll cnt=num[j];cnt<=s;cnt++)
dp[i][j][cnt]+=dp[i-1][k][cnt-num[j]];
//这里空间太多,没有滚动,正序也对
}
}
}
ll ans=0;
for(ll j=0;j<maxn;j++){
ans+=dp[n][j][s];
}
cout<<ans;
}
ll get_sum(ll x){
ll sum=0;
while(x){
sum+=(x&1);
x>>=1;
}
return sum;
}
int main(){
cin>>n>>s;
maxn=1<<n;
for(ll i=0;i<maxn;i++){
num[i]=get_sum(i);
}
solu();
}
2.3Mondriaan's Dream
2411 -- Mondriaan's Dream (poj.org)
291. 蒙德里安的梦想 - AcWing题库
Mondriaan's Dream
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 29311 | Accepted: 15608 |
Description
Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series' (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.
Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!
Input
The input contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.
Output
For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical tilings multiple times.
Sample Input
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0
Sample Output
1 0 1 2 3 5 144 51205