#COCI#玻璃杯(状压DP)

玻璃杯

【问题描述】

你有N个容量无限大的玻璃杯,每个玻璃杯都有一些水。你想要喝光所有的水,但是你最多只能喝k玻璃杯。怎么办呢?你可以把一个玻璃杯的水全部倒入另一个玻璃杯,。但是你将第i玻璃杯中的水倒入第j玻璃杯,需要花费代价Cij。如何花费最少的代价,让你能喝光所有的水。

【输入】

第一行包含整数N,K(1<=K<=N<=20)

接下来N行,每行包含N个整数Cij0<=Cij<=105)。第i行第j列的数表示CijCii一定是0.

【输出】

输出最小的代价。

40%的数据,N<=10

【输入输出样例1】

drink.in

drink.out

3 3

0 1 1

1 0 1

1 1 0

 

0

【输入输出样例2

drink.in

drink.out

3 2

0 1 1

1 0 1

1 1 0

1

【输入输出样例3

drink.in

drink.out

5 2

0 5 4 3 2

7 0 4 4 4

3 3 0 1 2

4 3 1 0 5

4 5 5 5 0

5


很简单的状压DP,Dp[state]表示到达当前状态的最小花费,state二进制的每一位表示一个杯子是否有水(1有0无)

每次转移将当前的一个1变成0(将这个杯子里的水倒进当前状态中为1的杯子里)

或许会有疑问,为什么不能倒进0呢,其实这种情况会在其它时候被枚举到的,想一想。


Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int Max = 20;
const int INF = 0x3f3f3f3f;

int N, K;
int Dp[1 << Max], C[Max + 5][Max + 5];
bool vis[1 << Max];

void getint(int & num){
    char c; int flg = 1;    num = 0;
    while((c = getchar()) < '0' || c > '9') if(c == '-')    flg = -1;
    while(c >= '0' && c <= '9'){    num = num * 10 + c - 48;   c = getchar();}
    num *= flg;
}

int Get_Dp(int S){
    if(vis[S])  return Dp[S];
    Dp[S] = INF;
    for(int i = 1; i <= N; ++ i)    if(! (S & (1 << (i - 1)))){
        int fro = S | (1 << (i - 1));
        for(int j = 1; j <= N; ++ j)    if(S & (1 << (j - 1)))
            Dp[S] = min(Dp[S], Get_Dp(fro) + C[i][j]);
    }
    vis[S] = 1;
    return Dp[S];
}

bool Check(int x){
    int cnt = 0;
    while(x){
        cnt += x & 1;
        x >>= 1;
    }
    return cnt == K;
}

int main(){
    //freopen("drink.in", "r", stdin);
    //freopen("drink.out", "w", stdout);
    getint(N), getint(K);
    for(int i = 1; i <= N; ++ i)
        for(int j = 1; j <= N; ++ j)
            getint(C[i][j]);
    int Up = 1 << N, Ans = INF;
    vis[(1 << N) - 1] = 1;
    for(int state = 1; state < Up; ++ state)    if(Check(state)){
        Get_Dp(state);
        Ans = min(Ans, Dp[state]);
    }
    printf("%d\n", Ans);
    return 0;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值