POJ3163 King of Fighters 状压DP/费用流

回归后的第一篇blog,康复打板中
这种题我做了2个多小时。。(反正我现在已经失去对题目难度的辨识了)
很容易想到 O(mn2n) O ( m n 2 n ) 的状压做法,但是超时,考虑优化,想到不正确的贪心思路,即n轮中每次都取最大概率,而最优解中不一定取这次的最大概率,但是会“退而求其次”,找没被选的里面的最优,而被选的最多为n-1个,所以对于每轮来说只要挑出概率前 n n 大的,最优解一定在其中选择,这样数据规模就从m减小到了至多 n2 n 2
除此以外,对数据取log后变为求累加的最大值,所以也可以建图后费用流
注意:数据给出的是对手获胜的概率,不是自己的
状压DP

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,ID,cnt;
int bit[1030],id[100005];
double data[11][100005],a[11][105],f[2][1030];
bool vis[100005];
bool cmp(int x,int y){return data[ID][x]>data[ID][y];}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for (int i=1;i<=n;++i)
            for (int j=1;j<=m;++j) scanf("%lf",data[i]+j),data[i][j]=1.0-data[i][j];
        for (int j=1,i=1;i<(1<<n);i<<=1,++j) bit[i]=j;
        for (int j=1;j<=m;++j) id[j]=j,vis[j]=0;
        cnt=0;
        for (ID=1;ID<=n;++ID)
        {
            sort(id+1,id+m+1,cmp);
            for (int j=1;j<=n;++j)
                if (!vis[id[j]])
                {
                    vis[id[j]]=1;
                    ++cnt;
                    for (int k=1;k<=n;++k) a[k][cnt]=data[k][id[j]];
                }
        }
        for (int i=0;i<(1<<n);++i) f[0][i]=0;
        f[0][0]=1;
        for (int x=1,i=1;i<=cnt;++i,x^=1)
            for (int k,j=0;j<(1<<n);++j)
            {
                f[x][j]=f[x^1][j];
                for (k=j;k;k^=(k&-k))
                    f[x][j]=max(f[x][j],f[x^1][j^(k&-k)]*a[bit[k&-k]][i]);
            }
        printf("%.15lf\n",f[cnt&1][(1<<n)-1]);
    }
}

费用流

#include<cstdio>
#include<iostream>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,ID,cnt,S,T,tot;
int id[100005],up[205],first[205];
double data[11][100005],a[11][105],dis[205];
bool vis[100005];
bool cmp(int x,int y){return data[ID][x]>data[ID][y];}
struct edge{
    int v,next;
    char w;
    double c;
}e[10005];
void add(int x,int y,int z,double c)
{
    e[++tot].v=y;e[tot].next=first[x];e[tot].w=z;e[tot].c=c;first[x]=tot;
    e[++tot].v=x;e[tot].next=first[y];e[tot].w=0;e[tot].c=-c;first[y]=tot;
}
queue<int>q;
bool spfa()
{
    for (int i=1;i<=T;++i) dis[i]=1e9;
    q.push(S);vis[S]=1;
    for (int x;!q.empty();q.pop())
    {
        x=q.front();
        for (int i=first[x];i;i=e[i].next)
            if (e[i].w&&dis[e[i].v]>dis[x]+e[i].c)
            {
                dis[e[i].v]=dis[x]+e[i].c;
                up[e[i].v]=i;
                if (!vis[e[i].v])
                    q.push(e[i].v),
                    vis[e[i].v]=1;
            }
        vis[x]=0;
    }
    return dis[T]<1e8;
}
double flow()
{
    for (int i=up[T];i;i=up[e[i^1].v]) swap(e[i].w,e[i^1].w);
    return dis[T];
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for (int i=1;i<=n;++i)
            for (int j=1;j<=m;++j) scanf("%lf",data[i]+j),data[i][j]=1.0-data[i][j];
        for (int j=1;j<=m;++j) id[j]=j,vis[j]=0;
        cnt=0;
        for (ID=1;ID<=n;++ID)
        {
            sort(id+1,id+m+1,cmp);
            for (int j=1;j<=n;++j)
                if (!vis[id[j]])
                {
                    vis[id[j]]=1;
                    ++cnt;
                    for (int k=1;k<=n;++k) a[k][cnt]=data[k][id[j]];
                }
        }
        S=0;T=n+cnt+1;tot=1;
        for (int i=S;i<=T;++i) vis[i]=first[i]=0;
        for (int i=1;i<=n;++i) add(S,i,1,0);
        for (int i=1;i<=cnt;++i) add(i+n,T,1,0);
        for (int i=1;i<=n;++i)
            for (int j=1;j<=cnt;++j) add(i,j+n,1,-log(a[i][j]));
        double ans=0;
        while(spfa()) ans+=flow();
        printf("%.15lf\n",exp(-ans));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值