hdu4281 状态压缩dp(mtsp)+树状数组

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=17;
/*这里参考大佬的代码,去掉初始位进行二进制枚举*/
int n,m,maxs,lim,cnt;
int x[maxn],y[maxn],c[maxn];
int dp[1<<maxn];///某状态下的最少使用教练
int ds[1<<maxn];///某状态下的最少路程花费
int dpp[1<<maxn][maxn];///状态s下,当前在i点的最小路程花费
int dd[1<<maxn];///解决状态s并返回起点的最小路程花费
int bin[1<<maxn];
int state[1<<maxn],dis[maxn][maxn];

bool isok(int st){///位枚举
    int sum=0,idx=1;
    while(st){
        if(st&1) sum+=c[idx];
            st>>=1;
            idx++;
        }
    if(sum>m) return false;
    return true;
}

void Init(){
	for(int i=0;i<16;i++)
		bin[1<<i]=i+1;
}
void makesta(){///树状数组处理
	cnt=0;
	for(int i=0;i<1<<(n-1);i++){
		int tmp=i,tot=0;
		while(tmp){
			int x=tmp&(-tmp);
			tot+=c[bin[x]];
			tmp-=x;
		}
		if(tot<=m)state[++cnt]=i;
	}
}

void get_dis(){
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        dis[i][j]=ceil(sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));
}

void get_dd(){
    for(int i=1;i<n;i++) dpp[1<<(i-1)][i-1]=dis[0][i];
    for(int i=0;i<lim;i++){
        for(int j=0;j<n-1;j++){
        if(!(i&(1<<j))) continue;
        dd[i]=min(dd[i],dpp[i][j]+dis[j+1][0]);
        for(int k=0;k<n-1;k++){
            if(i&(1<<k)) continue;
            dpp[i|(1<<k)][k]=min(dpp[i|(1<<k)][k],dpp[i][j]+dis[j+1][k+1]);
        }
    }
    }
}

int main(){
    Init();
    while(scanf("%d%d",&n,&m)!=EOF){
        for(int i=0;i<n;i++) scanf("%d%d",&x[i],&y[i]);
        get_dis();
        maxs=0;
        for(int i=0;i<n;i++){
            scanf("%d",&c[i]);
            maxs=max(maxs,c[i]);
        }
        if(maxs>m){
            printf("-1 -1\n");
            continue;
        }
        lim=1<<(n-1);
        //makesta();
        cnt=0;
        for(int i=0;i<lim;i++) if(isok(i)) state[++cnt]=i;

        memset(dp,inf,sizeof(dp));
        memset(ds,inf,sizeof(ds));
        memset(dpp,inf,sizeof(dpp));
        memset(dd,inf,sizeof(dd));

        get_dd();

        dp[0]=ds[0]=0;
        for(int i=0;i<lim;i++)
        for(int j=1;j<=cnt;j++){
            if(i&state[j]) continue;
            dp[i|state[j]]=min(dp[i|state[j]],dp[i]+1);
            ds[i|state[j]]=min(ds[i|state[j]],ds[i]+dd[state[j]]);
        }
        if(dp[lim-1]==inf) printf("-1 ");
        else printf("%d ",dp[lim-1]);
        if(ds[lim-1]==inf) printf("-1\n");
        else printf("%d\n",ds[lim-1]);
    }
    return 0;
}
#include<algorithm>
#include<iostream>
#include<string.h>
#include<sstream>
#include<stdio.h>
#include<math.h>
#include<vector>
#include<string>
#include<time.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=100010;
int  n,m,cnt,ans1,ans2,lim;
int x[20],y[20],c[20];
int state[1<<17];//存可行状态
int dp[1<<17],dis[20][20];//dp处理最少的裁判数
int dpp[20][1<<17],mind[1<<17];//dpp[i][j]表示当前在i点。访问状态为j时的最小距离。
//clock_t st,ed;               //mind[i]表示完成访问状态i所走的最少路程
//double dur;

void debug(){
    cout<<"***"<<endl;
    for(int i=0;i<cnt;i++) cout<<state[i]<<endl;cout<<"***"<<endl;
}

bool isok(int st)//判断一个裁判的可行状态。抽象为一个物品。
{
    int i=0,sum=0;

    while(st)
    {
        if(st&1)
            sum+=c[i];
        st>>=1;
        i++;
    }
    if(sum<=m)
        return true;
    else
        return false;
}
void getDist()//求距离
{
    int i,j,xx,yy;
    for(i=0;i<n;i++)
    {
        dis[i][i]=0;
        for(j=i+1;j<n;j++)
        {
            xx=x[j]-x[i];
            yy=y[j]-y[i];
            dis[i][j]=dis[j][i]=ceil(sqrt((double)(xx*xx+yy*yy)));
        }
    }
}
void TSP()//典型的单旅行商问题
{
    int s,j,ns;
    memset(dp,0x3f,sizeof dp);
    dp[0]=0;
    for(s=0;s<lim;s++)
    {
        if(dp[s]==INF)
            continue;
        for(j=0;j<cnt;j++)
        {
            if(s&state[j])///有重叠
                continue;
            ns=s|state[j];
            dp[ns]=min(dp[ns],dp[s]+1);
        }
    }
    ans1=dp[lim-1];
}
void MTSP()//多旅行商
{
    int i,j,k,s,ns;
    getDist();
    memset(dpp,0x3f,sizeof dpp);
    memset(mind,0x3f,sizeof mind);
    dpp[0][1]=0;//0必须为起点
    for(i=0;i<cnt;i++)
    {
        s=state[i];
        for(j=0;j<n;j++)
        {
            if(s&(1<<j)&&dpp[j][s]!=INF)///枚举最后一步伐
            {
                mind[s]=min(mind[s],dpp[j][s]+dis[j][0]);//把一个裁判完成访问状态再回到0点抽象为一个物品
                for(k=0;k<n;k++)
                {
                    if(s&(1<<k))
                        continue;
                    ns=s|(1<<k);//ns状态不一定有效
                    dpp[k][ns]=min(dpp[k][ns],dpp[j][s]+dis[j][k]);
                }
            }
        }
    }
    for(s=1;s<lim;s++)///枚举s的子集的划分,ns和s-ns
    {
        if(s&1)       
        {
            for(ns=(s-1)&s;ns;ns=(ns-1)&s)
                mind[s]=min(mind[s],mind[ns|1]+mind[(s-ns)|1]);
        }
    }
   ///这是我的DP方法极限数据18s。。汗。。
    ans2=mind[lim-1];
}
int main()
{
    int i,ma;

    while(~scanf("%d%d",&n,&m))
    {
        for(i=0;i<n;i++)
            scanf("%d%d",&x[i],&y[i]);
        ma=0;
        for(i=0;i<n;i++)
        {
            scanf("%d",&c[i]);
            ma=max(ma,c[i]);
        }
        if(ma>m)
        {
            printf("-1 -1\n");
            continue;
        }
        cnt=0,lim=1<<n;
        for(i=0;i<lim;i++)
            if(isok(i))
                state[cnt++]=i;debug();
        TSP();
        MTSP();
        printf("%d %d\n",ans1,ans2);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值