codeforces 417D Cunning Gena

题意:有m道题,要分给n个朋友解决,每个朋友有不同的条件,酬劳,且每个朋友要至少k个显示器连接作者电脑才肯帮忙,所以作者需要买至少k个显示器。求解决所有问题所需最少的钱。

分析:可以状压20个问题,实际是二维,dp[i][s]表示用前i个人解决s(二进制,解决问题的集合)所需最少钱,由于爆内存可省略第一维。正确的做法是先根据显示器数目将朋友排序,我纠结的是不排序维护一个mx数组表示s状态显示器的最大值会wa在第24发。

正确代码:

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<map>
#include<algorithm>
#include<queue>
#include<set>
#define inf 2e18
#define pi acos(-1.0)
#define eps 1e-8
#define seed 131
using namespace std;
typedef pair<int,int> pii;
typedef unsigned long long ULL;
typedef long long LL;
const int maxn=100005;
int n,m,b;
LL dp[1<<20];
struct Node
{
    int x;
    int k;
    int s;
    bool operator<(const Node& a)const
    {
        return k<a.k;
    }
}fr[105];
int main()
{
    scanf("%d%d%d",&n,&m,&b);
    int u;
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&fr[i].x,&fr[i].k,&u);
        int ans=0;
        int d;
        for(int j=0;j<u;j++)
        {
            scanf("%d",&d);
            ans^=(1<<(d-1));
        }
        fr[i].s=ans;
    }
    sort(fr+1,fr+n+1);
    dp[0]=0;
    for(int i=1;i<(1<<m);i++)
        dp[i]=inf;
    LL res=inf;
    for(int i=1;i<=n;i++){
        for(int j=0;j<(1<<m);j++){
            dp[j|fr[i].s]=min(dp[j|fr[i].s],dp[j]+fr[i].x);//正推
        }
        res=min(res,dp[(1<<m)-1]+fr[i].k*(LL)b);
    }
    if(res==inf)
        res=-1;
    cout<<res;
    return 0;
}
错误代码:

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<map>
#include<algorithm>
#include<queue>
#include<set>
#define inf 2e18
#define pi acos(-1.0)
#define eps 1e-8
#define seed 131
using namespace std;
typedef pair<int,int> pii;
typedef unsigned long long ULL;
typedef long long LL;
const int maxn=100005;
int n,m,b;
LL dp[1<<20];
int mx[1<<20];
struct Node
{
    int x;
    int k;
    int s;
    bool operator<(const Node& a)const
    {
        return k<a.k;
    }
}fr[105];
int main()
{
    scanf("%d%d%d",&n,&m,&b);
    int u;
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&fr[i].x,&fr[i].k,&u);
        int ans=0;
        int d;
        for(int j=0;j<u;j++)
        {
            scanf("%d",&d);
            ans^=(1<<(d-1));
        }
        fr[i].s=ans;
    }
    //sort(fr+1,fr+n+1);
    dp[0]=0;
    mx[0]=0;
    for(int i=1;i<(1<<m);i++){
        dp[i]=inf;
        mx[i]=-1e9;
    }
    LL res=inf;
    for(int i=1;i<=n;i++){
        for(int j=0;j<(1<<m);j++){
            if(dp[j]<inf){
                LL sum=dp[j]+fr[i].x;
                if(fr[i].k>mx[j])
                    sum+=(fr[i].k-mx[j])*(LL)b;
                if(sum<dp[j|fr[i].s]){
                    dp[j|fr[i].s]=sum;
                    mx[j|fr[i].s]=max(fr[i].k,mx[j]);
                }
            }
        }
        res=min(res,dp[(1<<m)-1]);
    }
    if(res==inf)
        res=-1;
    cout<<res;
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值