匹配问题 dp预处理

来源:互联网 发布:sony xperia xz1 知乎 编辑:程序博客网 时间:2024/06/08 05:43

关键词:dp预处理:排序
题意:x轴上n个点与m个点之间进行匹配,保证1)n每个点都只能匹配一次2)m每个点都得有匹配对象(m<=n),已知两两点之间的匹配代价=坐标差得绝对值,求满足条件的最小匹配代价
解法:
失误:没有充分利用条件的特殊性:在x轴上!因此可以先把n个点和m个点分别在坐标轴上进行排序。
可以观察到连个端点之间一定相互匹配!
结论:x1<=x2,y1<=y2,则|x1-y1|+|x2-y2|<|x1-y2|+|x2-y1|
证明:简单处理一下就可以证出,略去
所以可以得到dp解法如下:
dp[i][j]:n个点中的前i个匹配m个点中的前j个的最小代价
dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+abs(pos1[i]-pos2[j])

注意:
1.long long范围内的INF1使用 0x0fffffffffffffff (15个f,9*INF超越LL最大值)
2.int范围内的INF2使用0x3f3f3f3f(INF2*3超越INT最大值)

拓展1:改成求匹配代价最大值,如何求解?
解法:由上述绝对值不等式可知,排好序后m个点中的最大值一定与n个点中的最小值进行匹配,否则匹配代价一定还可以增大
因此只需要把m个点从小到大排列,n个点从大到小排列,dp状态转移方程中的min改为max即可!

拓展2:改成二维平面内的坐标点,匹配代价是曼哈顿距离,如何求解最小匹配值?

#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>#define mem(a,b) memset(a,sizeof(a),b)#define ll long long#define MP make_pairusing namespace std;typedef long long lld;const lld INF = 0x0fffffffffffffff ;const int maxn = 4000+10;int n,m;struct Node{    int id;    long long pos;};bool cmp(Node a,Node b){    return a.pos<b.pos;}Node que[maxn],pos[maxn];long long  dp[maxn];bool path[maxn][maxn];int match[maxn];void print(int n,int m){    if(n==1) { match[que[1].id]=pos[1].id; return; }    if(path[n][m]==0){        print(n-1,m);    }    else if(path[n][m]==1){        print(n-1,m-1);    }    match[que[n].id]=pos[m].id;}int abs(int a){    if(a<0) return -a;    return a;}int main(){    //freopen("a.txt","r",stdin);    while(scanf("%d",&n)!=EOF){        for(int i=1;i<=n;i++) { scanf("%lld",&que[i].pos); que[i].id=i; }        scanf("%d",&m);        for(int i=1;i<=m;i++) { scanf("%lld",&pos[i].pos); pos[i].id=i; }        sort(que+1,que+n+1,cmp);        sort(pos+1,pos+m+1,cmp);        for(int j=0;j<=m;j++) dp[j]=INF;        dp[1]=abs(que[1].pos-pos[1].pos);        for(int i=2;i<=n;i++){            for(int j=min(m,i);j>=1;j--){                if(dp[j]<=dp[j-1]){ dp[j]=dp[j]+abs(que[i].pos-pos[j].pos); path[i][j]=0;}                else { dp[j]=dp[j-1]+abs(que[i].pos-pos[j].pos); path[i][j]=1; }            }        }        printf("%lld\n",dp[m]);        print(n,m);        for(int i=1;i<n;i++) printf("%d ",match[i]);        printf("%d\n",match[n]);    }    return 0;}
0 0
原创粉丝点击