玻璃杯
【问题描述】
你有N个容量无限大的玻璃杯,每个玻璃杯都有一些水。你想要喝光所有的水,但是你最多只能喝k个玻璃杯。怎么办呢?你可以把一个玻璃杯的水全部倒入另一个玻璃杯,。但是你将第i个玻璃杯中的水倒入第j个玻璃杯,需要花费代价Cij。如何花费最少的代价,让你能喝光所有的水。
【输入】
第一行包含整数N,K(1<=K<=N<=20)
接下来N行,每行包含N个整数Cij(0<=Cij<=105)。第i行第j列的数表示Cij。Cii一定是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;
}