斯坦纳树[全都floyd+状态dp]

http://endlesscount.blog.163.com/blog/static/821197872012525113427573/


poj3123 这个只有30个节点, 直接floyd即可, 后面找最优解还是比较恶心的, 不过一共就2^4个状态,再 循环4次搞定, 老外还有一种神代码, 看了好久才懂

不过最后看了别人的代码, 还有一种合并的方法

http://blog.renren.com/share/327827934/14113666814?from=0101010202&ref=minifeed&sfet=102&fin=1&ff_id=327827934



#include <cstdio>
#include <map>
#include <string>
#include <iostream>
#include <cstring>

#define lowbit(x) ((x)&(-x))
using namespace std;
const int maxn=50;
const int inf=0x3f3f3f3f;
map<string, int>cityid;

int dist[maxn][maxn];
int dp[300][maxn];
const int sta[]={0, 3, 12, 48, 192, 15, 51, 195, 60, 204, 240, 63, 207, 243, 252, 255};

int bit(int x)
{
    x=lowbit(x);
    int res=0;
    for (; x; x>>=1, res++);
    return res-1;
}
int main()
{
    int n, m;
    while(cin >> n >> m, n||m)
    {
        string a, b;
        int cnt, limit=256;
        memset (dist, 0x3f, sizeof(dist));
        cityid.clear();
        for (int i=0; i<n; ++i)
        {
            cin >> a;
            cityid[a]=i;
            dist[i][i]=0;
        }
        
        for (int i=0; i<m; ++i)
        {
            cin >> a >> b >> cnt;
            int fa=cityid[a], fb=cityid[b];
            dist[fb][fa]=dist[fa][fb]=min(dist[fa][fb], cnt);
        }

        for (int k=0; k<n; ++k)
            for (int i=0; i<n; ++i)
                for (int j=0; j<n; ++j)
                    dist[i][j]=min(dist[i][j], dist[i][k]+dist[k][j]);
        
        memset (dp, 0x3f, sizeof(dp));
        int info[10];
        for (int i=0; i<8; ++i)
        {
            cin >> a;
            info[i]=cityid[a];
            for (int j=0; j<n; ++j)
            {
                dp[1<<i][j]=dist[cityid[a]][j];
            }
        }

        for (int j=0; j<limit; ++j)
        {
            int c=0;
            if(j&(j-1)==0)continue;
            /// o(n*n*2^k) 没仔细估。
            for (int i=0; i<n; ++i)
            {
                for (int sub=j; sub; sub=(sub-1)&j)
                {/// 用压缩的子状态推出当前状态,这时并不是最优解, 会有些边被重复计算。
                    dp[j][i]=min(dp[j][i], dp[sub][i]+dp[j-sub][i]);
                }
                if(dp[j][i]<dp[j][c])c=i;
            }
            /// O(n*n*2^k)
            for (int i=0; i<n; ++i)
                for (int k=0; k<n; ++k)
                {///同状态用最小的状态去更新, 这时便会对有重复的边进行去重。
                    dp[j][k]=min(dp[j][k], dp[j][i]+dist[i][k]);
                }
        }
        int ans=inf;
        for (int p1=0; p1<16; ++p1)
        {
            for (int p2=0; p2<16; ++p2)
            {
                for (int p3=0; p3<16; ++p3)
                {
                    for (int p4=0; p4<16; ++p4)
                    {
                        if(sta[p1]+sta[p2]+sta[p3]+sta[p4]==255)
                        {                            
                            for (int i=0; i<n; ++i)
                            {
                                int tmp=0;
                                if(sta[p1]!=0)tmp+=dp[sta[p1]][info[bit(sta[p1])]];
                                if(sta[p2]!=0)tmp+=dp[sta[p2]][info[bit(sta[p2])]];
                                if(sta[p3]!=0)tmp+=dp[sta[p3]][info[bit(sta[p3])]];
                                if(sta[p4]!=0)tmp+=dp[sta[p4]][info[bit(sta[p4])]];
                                
                                ans=min(tmp, ans);
                            }
                        }
                    }
                }
            }
        }
        cout << ans << endl;
        //cout << dp[243][4] << endl;
        //cout << dp[12][2] << endl;
    }
    return 0;
}
/*
6 6
1 2 3 4 5 6
1 2 1
2 3 1
3 4 1
4 6 1
6 5 1
1 5 1
5 6
2 3
5 6
6 4

*/

hdu  4085  3次dp 算上floyd的话 4次了


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn=55;
const int inf=0x3f3f3f3f;

int N, M, K;
int dist[maxn][maxn];
int dp[1234][maxn];

bool check(int x)
{
    ///int mask1=x&((1<<K)-1), mask2=x&((1<<(K*2))-(1<<K));
    int cnt1=0, cnt2=0;
    for (int i=0; i<K; ++i)
    {
        if(x&(1<<i))cnt1++;
        if(x&(1<<i+K))cnt2++;
    }
    //printf("%d %d %d\n", x, cnt1, cnt2);
    return cnt1==cnt2;
}

int main()
{
    int cas;    scanf("%d", &cas);
    while (cas--)
    {
        scanf("%d%d%d", &N, &M, &K);
        memset (dist, 0x3f, sizeof(dist));
        for (int i=0; i<M; ++i)
        {
            int a, b, c; scanf("%d%d%d", &a, &b, &c);
            a--, b--;
            dist[a][b]=dist[b][a]=min(dist[a][b], c);
            
        }
        for (int i=0; i<N; ++i)dist[i][i]=0;
        
        for (int k=0; k<N; ++k)
            for (int i=0; i<N; ++i)
                for (int j=0; j<N; ++j)
                    dist[i][j]=min(dist[i][k]+dist[k][j], dist[i][j]);
        
        int limit=1<<(K<<1);
        memset (dp, 0x3f, sizeof(dp));
        for (int i=0; i<K; ++i)
        {
            for (int j=0; j<N; ++j)
            {
                dp[1<<i][j]=dist[i][j];
                dp[1<<(i+K)][j]=dist[N-K+i][j];
            }
        }
        for (int i=0; i<limit; ++i)///if(i&(i-1))
        {
            for (int j=0; j<N; ++j)
            {
                for (int sub=i&(i-1); sub; sub=(sub-1)&i)
                {
                    dp[i][j]=min(dp[i][j], dp[i^sub][j]+dp[sub][j]);
                }
            }
            for (int j=0; j<N; ++j)
            {
                for (int k=0; k<N; ++k)
                {
                    dp[i][j]=min(dp[i][j], dp[i][k]+dist[k][j]);
                }
            }
        }
        int d[1234];
        memset (d, 0x3f, sizeof(d));
        for (int i=0; i<limit; ++i)
            if(check(i))
                for (int j=0; j<N; ++j)
                    d[i]=min(d[i], dp[i][j]);
        
        for (int i=0; i<limit; ++i)
        {
            if(check(i))
            {
                for (int j=0; j<limit; ++j)
                {
                    if(check(j))
                        if((i|j)==i && (i&j)!=i)
                        {
                            d[i]=min(d[i], d[j]+d[j^i]);
                        }
                }
            }
        }
//        int ans=0x3f3f3f3f;
//        for (int i=0; i<N; ++i)
//        {
//            ans=min(ans, dp[limit-1][i]);
//        }
        if(d[limit-1]==inf)puts("No solution");
        else printf("%d\n", d[limit-1]);
    }
    return 0;
}

/*
10
4 3 2
1 2 3
2 3 4
3 4 5

4 3 1
4 2 10
3 1 9
2 3 10

6 2 2
1 5 1000
2 6 1000
6 2 2
1 5 1000
2 5 1000


*/

zju 3613Wormhole Transport

同一个算法 就是判断变一下而已

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=222;
int N;
int dist[maxn][maxn];
int dp[1<<10][maxn];
int ans[1<<10];

bool check(int a, int pre)
{
    int cnt1=0, cnt2=0;
    for (int i=0; a; a>>=1, ++i)
    {
        if(a&1)
        {
            if(i<pre)cnt1++; 
            else cnt2++;
        }
    }
    return cnt1==cnt2;
}

int main()
{
    while (~scanf("%d", &N))
    {
        int fac[16], rsc[16];
        int cnt1=0, cnt2=0;
        memset (dist, 0x3f, sizeof(dist));
        int pro=0;
        for (int i=0; i<N; ++i)
        {
            int p, s; scanf("%d%d", &p, &s);
            ///if(s==1 && p>=1)s-=1, p-=1, pro+=1;
            while (p--)fac[cnt1++]=i;
            while (s--)rsc[cnt2++]=i;
            dist[i][i]=0;
        }
        int m; scanf("%d", &m);
        for (int i=0; i<m; ++i)
        {
            int a, b, c; scanf("%d%d%d", &a, &b, &c);
            a--, b--;
            dist[a][b]=dist[b][a]=min(dist[a][b], c);
        }
        for (int k=0; k<N; ++k)
            for (int i=0; i<N; ++i)
                for (int j=0; j<N; ++j)
                    dist[i][j]=min(dist[i][j], dist[i][k]+dist[k][j]);
        
        for (int i=0; i<cnt2; ++i)
        {
            fac[cnt1+i]=rsc[i];
        }
        memset (dp, 0x3f, sizeof(dp));
        
        for (int i=0; i<cnt2+cnt1; ++i)
        {
            for (int j=0; j<N; ++j)
            {
                dp[1<<i][j]=dist[fac[i]][j];
            }
        }
        int limit=1<<(cnt1+cnt2);
        for (int i=0; i<limit; ++i)
        {
            for (int j=0; j<N; ++j)
            {
                for (int sub=(i-1)&i; sub; sub=(sub-1)&i)
                {
                    dp[i][j]=min(dp[i][j], dp[sub][j]+dp[sub^i][j]);
                }
            }
            
            for (int j=0; j<N; ++j)
                for (int k=0; k<N; ++k)
                    dp[i][j]=min(dp[i][j], dp[i][k]+dist[k][j]);
        }
        //for (int i=0; i<limit; ++i)
        memset (ans, 0x3f, sizeof(ans));
        for (int i=0; i<limit; ++i)if(check(i, cnt1))
            for (int j=0; j<N; ++j)
                ans[i]=min(dp[i][j], ans[i]);
        
        for (int i=0; i<limit; ++i)if(check(i, cnt1))
        {
            for (int sub=(i-1)&i; sub; sub=(sub-1)&i)if(check(sub, cnt1))
            {
                ans[i]=min(ans[i], ans[sub]+ans[sub^i]);
            }
        }
        int cost=0;
        
        for (int i=0; i<limit; ++i)if(check(i, cnt1))if(ans[i]!=inf)
        {
            int tmp=0;
            for (int j=0; j<cnt1; ++j)if((1<<j)&i)tmp++;
            //printf("%d %d %d\n", i, tmp, ans[i]);
            if(pro<tmp)
            {
                pro=tmp;
                cost=ans[i];
            }
            if(pro==tmp)cost=min(cost, ans[i]);
        }
        printf("%d %d\n", pro, cost);
    }
    return 0;
}
/*
2
1 0
0 1
1
1 2 3

3
1 1
1 1
1 1
2
1 2 3
2 3 1
*/




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值