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;i<=5;++i)
{
if(a[1]==a[i+1])
{
dp[1<<(i-1)][2]=1;
}
}
for(int i=2;i<=n;++i)
{
for(int j=0;j<(1<<10);++j)//WA cao,这里是10,不是n,,,,,,
{
if(dp[j][i])
{
if(j&1)
{
dp[j>>1][i+1]=1;
continue;
}
else
{
int tt=0;
for(int k=1;k<=9&&i+k<=n;++k)//
{
if(!(j&(1<<k))&&(a[i]==a[i+k])&&(k-tt<=5))
{
dp[(j>>1)+(1<<(k-1))][i+1]=1;
}
else if(j&(1<<k))tt++;
}
}
}
}
}
if(dp[0][n+1]==1)cout<<"1"<<endl;
else cout<<"0"<<endl;
}
return 0;
}
14.射击外星人(难度较大思路很好想:难点在于优化,核心运用贪心的思想,状态转移过程也需要细节优化,删减无用状态在这里不好用,另外数组开小TLE了好久)
#include <cstdio>
#include <cmath>
#define ll long long
using namespace std;
const double INF=999999999;
struct point
{
double x,y;
}a[50];
double v[50][50];
double dp[1100000];
int sta[600000];
double getDis(point a,point b)
{
double ans=sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
return ans;
}
bool cmp(point a,point b)
{
point p;
p.x=0,p.y=0;
return getDis(a,p)<getDis(b,p);
}
void init(int n,point s)
{
for(int i=1;i<=n*2;++i)
{
for(int j=1;j<=n*2;++j)
{
v[i][j]=getDis(s,a[i])+getDis(a[i],a[j]);
}
}
}
int main()
{
/*
思路:
状态:dp【sta】
转移方程:dp【sta+j+k】=min{dp【sta】+v【j】【k】};
目标状态:dp【满状态】
步骤:
*输入:n次射击、2*n组坐标point a[2n].x/y、dp【sta】初始化极大值;
*边界处理:含有2个1的sta;
*权值v【】【】预处理:
调用计算两点间距离double getDis(point,point)
*状态转移:
转移条件:sta中不含j点、k点
*输出结果
*/
int t;
cin>>t;
int cas=0;
while(t--)
{
int sx,sy;
cin>>sx>>sy;
point start;
start.x=sx;
start.y=sy;
int n;
cin>>n;
for(int i=1;i<=2*n;++i)cin>>a[i].x>>a[i].y;
sort(a+1,a+1+2*n,cmp);//这里最关键,贪心优化
for(int i=0;i<(1<<(2*n));++i)dp[i]=INF;
init(n,start);
dp[0]=0;
for(int i=0;i<(1<<(2*n));++i)
{
if(dp[i]==INF)continue;//这里很关键
int j=0;
while(((i>>j)&1) &&(j<2*n-1))j++;//贪心优化
for(int k=j+1;k<n*2;++k)
{
if(!(i&(1<<j))&&!(i&(1<<k))&&j!=k)
{
double temp=min(dp[i]+v[j+1][k+1],dp[i]+v[k+1][j+1]);
dp[i+(1<<k)+(1<<j)]=min(dp[i+(1<<j)+(1<<k)],temp);
}
}
}
printf("Case #%d: %.2lf\n",++cas,dp[(1<<(2*n))-1]);
}
return 0;
}
17.郑厂长排兵布阵
(直接套炮兵阵地的模板,主要改动的地方:状态之间的关系、筛选状态的条件;WA原因:筛选状态的数组开小、统计可行状态中1数量的数组开小、行之间状态的关系判断少步骤(区分自身有误相邻的判断))。
#include <cstdio>
#include <cmath>
#include <cstring>
#define ll long long
using namespace std;//为什么要开三维数组,因为二维无法表示对应状态限制下的子解
int a[600];
int num[1000];
int sta[200];
int dp[110][200][200];
int getNum(int x,int n)//状态中1的个数
{
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>>2)))continue;
else
{
a[++tot]=i;
num[i]=getNum(i,n);
}
}
return tot;
}
int main()
{
int n,m;
while(cin>>n>>m)//n行,m列
{
memset(sta,0,sizeof(sta));
memset(dp,-1,sizeof(dp));
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
int x;
cin>>x;
if(x==1)x=0;
else x=1;
sta[i]+=x*(1<<(m-j));
}
//sta[i]=~sta[i];
}
int tot=init(m); //cout<<tot<<endl;
int maxx=0;
for(int i=1;i<=tot;++i)//第一行
{
if(n==1)//只有一行的情况要特判
{
if(!(sta[1]&a[i]))maxx=max(maxx,getNum(a[i],m));
continue;
}
for(int j=1;j<=tot;++j)//第二行
{
if( !(sta[1]&a[i]) && !(sta[2]&a[j]) && !((a[i]>>1)&a[j]) &&!(a[i]&(a[j]>>1)))
{
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)//当前
{
if(a[j]&sta[i])continue;
for(int k=1;k<=tot;++k)//上一
{
if(a[k]&sta[i-1])continue;
if((a[j]&(a[k]>>1))||((a[j]>>1)&a[k]))continue;
for(int l=1;l<=tot;++l)//上二
{
//if(a[l]&sta[i-2])continue;
if(a[l]&a[j])continue;
if(dp[i-1][k][l]!=-1)
{
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;
}
}
}
}
}
cout<<maxx<<endl;
}
return 0;
}