这题一开始状态想对了,但是因为是n^2,所以直接被否定了,但是最后居然看到时间20000ms= =。
定义状态f[i][j]代表处理完了前i个队,分配到j个避难处的最短路径和,那么不难想到状态转移是f[i][j]=min(f[i-1][j],f[i-1][j-1])+abs(d[i]-d[j]),f[i][j]只能由两种状态转移过来,处理完i-1个队,分配了j-1个避难处,当处理i时,可能i会被分配到之前的j-1个避难处之一,或者重新分配一个避难处给i。所以这样进行dp递推就可以了。并且在dp时,需要同时处理路径,记录下当前i这个i去的是哪个避难处。具体操作是,当i的避难处在j-1中时,那必然是那必然是在j-1,否则距离会增加,当需要重新分配一个避难处时,那么必然是在j,最后dfs处理一下,排序一下就可以输出了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define ret(i,a,b) for(int i=(a);i>=(b);i--)
#define ss(x) scanf("%d",&x)
#define ssl(x) scanf("%lld",&x)
const int maxn=4000+5;
const long long inf=(1ll<<60);
struct node{
ll d;
int id;
int belong;
}p[maxn],s[maxn];
ll f[2][maxn];
short path[maxn][maxn];
int n,m;
int cmpd(node a,node b) {return a.d<b.d;}
int cmpid(node a,node b) {return a.id<b.id;}
void dfs(int i,int j)
{
if(i!=1) dfs(i-1,path[i][j]);
p[i].belong=s[j].id;
}
int main()
{
while(ss(n)!=EOF)
{
rep(i,1,n) {ssl(p[i].d);p[i].id=i;}
ss(m);rep(i,1,m) {ssl(s[i].d);s[i].id=i;}
sort(p+1,p+1+n,cmpd);
sort(s+1,s+1+m,cmpd);
rep(i,1,m) f[0][i]=inf;
f[0][0]=0;
rep(i,1,n){
int now=i&1;
rep(j,0,m) f[now][j]=inf;
rep(j,1,min(i,m)){//这里要初始化1~m,如果是min(i,m)的话,下次使用的时候有一个位置是0了
ll temp=abs(p[i].d-s[j].d);
if(f[now^1][j-1]<=f[now^1][j]){
f[now][j]=f[now^1][j-1]+temp;
path[i][j]=j-1;
}
else{
f[now][j]=f[now^1][j]+temp;
path[i][j]=j;
}
}
}
dfs(n,m);
sort(p+1,p+1+n,cmpid);
printf("%lld\n",f[n&1][m]);
rep(i,1,n) printf("%s%d",(i==1)?"":" ",p[i].belong);printf("\n");
}
return 0;
}