[COCI2016-2017#3] Kroničan 解题记录

[COCI2016-2017#3] Kroničan 解题记录


题意简述

N N N 个装有水的杯子,你需要通过从一个杯子向另一个杯子倒水,使这些杯子里面至多有 K K K 个有水,从杯子 i i i 往杯子 j j j 倒水的代价是 C i , j C_{i,j} Ci,j


题目分析

数据范围: 1 ≤ N ≤ 20 1 \leq N \leq 20 1N20,一眼状压。
d p S dp_S dpS 表示当前装有水的杯子集合为 S S S,每次枚举从杯子 k ∈ [ 1 , n ] k \in [1,n] k[1,n] 向杯子 j ∈ [ 1 , n ] j \in [1,n] j[1,n]倒水。因为一开始所有杯子都有水,越到后面水越少,所以考虑倒序转移,即从 2 n − 1 2^n-1 2n1 1 1 1 转移。
状态转移方程:
d p S ⨁ j ← min ⁡ k = 1 n d p S + C j , k dp_{S \bigoplus {j}} \gets \min \limits _{k=1}^{n}dp_{S}+C_{j,k} dpSjk=1minndpS+Cj,k
对于最后统计答案,我们可以用 __builtin_popcount() 函数(我这里是手写的),对于二进制中 1 1 1 的个数小于等于 K K K 的更新。


AC Code
#include<bits/stdc++.h>
#define arrout(a,n) rep(i,1,n)std::cout<<a[i]<<" "
#define arrin(a,n) rep(i,1,n)std::cin>>a[i]
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define dep(i,x,n) for(int i=x;i>=n;i--)
#define erg(i,x) for(int i=head[x];i;i=e[i].nex)
#define dbg(x) std::cout<<#x<<":"<<x<<" "
#define mem(a,x) memset(a,x,sizeof a)
#define all(x) x.begin(),x.end()
#define arrall(a,n) a+1,a+1+n
#define PII std::pair<int,int>
#define m_p std::make_pair
#define u_b upper_bound
#define l_b lower_bound
#define p_b push_back
#define CD const double
#define CI const int
#define int long long
#define il inline
#define ss second
#define ff first
#define itn int
CI N=25;
int n,K,ans=LLONG_MAX,c[N][N],dp[(1<<N-5)+5];
int calc(int x){
    int cnt=0;
    rep(i,0,20){
        if((x>>i)&1){
            cnt++;
        }
    }
    return cnt;
}
signed main() {
    std::cin>>n>>K;
    rep(i,1,n) {
        rep(j,1,n) {
            std::cin>>c[i][j];
        }
    }
    mem(dp,0x3f);
    dp[(1<<n)-1]=0;
    dep(i,(1<<n)-1,1) {
        rep(j,1,n) {
            if((i>>j-1)&1){
                continue;
            }
            rep(k,1,n) {
                if(!((i>>k-1)&1)) {
                    continue;
                }
                dp[i]=std::min(dp[i],dp[i|(1<<j-1)]+c[j][k]);
            }
        }
    }
    rep(i,1,(1<<n)-1) {
        if(calc(i)<=K) {
            ans=std::min(ans,dp[i]);
        }
    }
    std::cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值