剑之修炼
jzoj 2130
题目大意:
在一个位置上有一个人,同时还有NNN(N⩽10N \leqslant 10N⩽10)个怪物,这个人会不停地释放技能,技能可以瞬间杀死周围8个格子上的怪物,行走速度是每个单位时间走一个单位距离,现在问这个人最快要多久才能杀死所有怪物(还要输出路径)
输入:
输入是先输入地图范围S和怪兽数量N,然后输入此人位置,接下来n行,每行表示怪兽的位置
输入样例#1
5 32 21 13 31 2
输出样例#1
0
输入样例#2
5 3
3 3
1 1
5 5
1 5
输出样例#2
6
3 2
2 2
2 3
2 4
3 4
4 4
数据范围
5⩽S⩽305 \leqslant S \leqslant 305⩽S⩽30
N⩽10N \leqslant 10N⩽10
解题思路:
用状压DP压缩杀怪状态,然后设f[s][i][j]f[s][i][j]f[s][i][j]为杀怪状态为s,且现在在iii怪的j方向上j方向上j方向上,然后每一次枚举从哪个位置到那个位置,然后路径只要在DP的时候记录一下即可
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#define abs(x) (x)<0?-(x):(x)
using namespace std;
int n,m,ans,sum,x[20],y[20],f[(1<<12)][20][10],s[(1<<12)][20][10],s1[(1<<12)][20][10];
const int dx[9]={0,1,1,1,0,-1,-1,-1,0};
const int dy[9]={1,1,0,-1,-1,-1,0,1,0};
int dis(int from,int fw,int to,int tw)//计算两个点的距离
{int x1,x2,y1,y2;x1=x[from]+dx[fw];y1=y[from]+dy[fw];x2=x[to]+dx[tw];y2=y[to]+dy[tw];if ((abs(x1-x2))+(abs(y1-y2))<0){x1=1;}return (abs(x1-x2))+(abs(y1-y2));
}
void dg(int S,int dep,int w)
{if (S==1) return;int x1,x2,y1,y2,xx,yy;dg(S-(1<<dep),s[S][dep][w],s1[S][dep][w]);//递归下去x1=x[s[S][dep][w]]+dx[s1[S][dep][w]];//两个点的坐标(from和to)y1=y[s[S][dep][w]]+dy[s1[S][dep][w]];x2=x[dep]+dx[w];y2=y[dep]+dy[w];if (x1<x2) xx=1;//预处理else xx=-1;if (y1<y2) yy=1;else yy=-1;while (x1!=x2)//往x2走{x1+=xx;printf("\n%d %d",x1,y1);}while (y1!=y2){y1+=yy;printf("\n%d %d",x1,y1);}
}
int main()
{memset(f,127/3,sizeof(f)); scanf("%d %d %d %d",&m,&n,&x[0],&y[0]);f[1][0][8]=0;for (int i=1;i<=n;++i)scanf("%d %d",&x[i],&y[i]);for (int i=1;i<=(1<<n+1)-1;++i)//枚举状态for (int to=1;to<=n;++to)//枚举到的点if (i&(1<<to))//存在for (int from=0;from<=n;++from)//从哪里来if ((i&(1<<from)||from==0)&&from!=to)//存在且不相同for (int tw=0;tw<=8;++tw)//在to的那个方向if (x[to]+dx[tw]>0&&x[to]+dx[tw]<=m&&y[to]+dy[tw]>0&&y[to]+dy[tw]<=m)//没出界for (int fw=0;fw<=8;++fw)//在from的那个方向if (x[from]+dx[fw]>0&&x[from]+dx[fw]<=m&&y[from]+dy[fw]>0&&y[from]+dy[fw]<=m)//没出界if (f[i-(1<<to)][from][fw]+dis(from,fw,to,tw)<f[i][to][tw])//更优{f[i][to][tw]=f[i-(1<<to)][from][fw]+dis(from,fw,to,tw);//更新s[i][to][tw]=from;//记录路径s1[i][to][tw]=fw;}int k=(1<<n+1)-1;for (int i=1;i<=n;++i)for (int j=0;j<=9;++j)if (f[k][ans][sum]>f[k][i][j])//取最大ans=i,sum=j;printf("%d",f[k][ans][sum]);if (f[k][ans][sum]) dg(k,ans,sum);//递归输出return 0;
}