当前位置 : 主页 > 大数据 > 区块链 >

模拟赛毒瘤状压DP题:Kronican

来源:互联网 收集:自由互联 发布时间:2021-06-22
Kronican 内存限制:32 MiB 时间限制:2000 ms 标准输入输出 题目类型:传统 评测方式:文本比较 上传者: cqbzgm 题目描述 Mislav有N个无限体积的杯子,每一个杯子中都有一些水。Mislav想喝掉

Kronican
内存限制:32 MiB
时间限制:2000 ms
标准输入输出
题目类型:传统
评测方式:文本比较
上传者: cqbzgm
题目描述
Mislav有N个无限体积的杯子,每一个杯子中都有一些水。Mislav想喝掉所有的水,但他不想喝超过K杯水。Mistrav能做的就是将一个杯子中的水倒入另一个杯子中。 不幸的是,挑选哪两个杯子进行倒水操作对Mislav来说很重要,因为并非所有的杯子都离他一样远。更准确地说,从i号杯子向j号杯子倒水所付出的代价为Cij。 帮助Mislav找到他需要付出的总代价的最小值。

输入格式
第一行输入包含整数N和K(1≤K≤N≤20)。表示水杯的总数和Mislav最多能喝多少杯。 接下来N行每行包含N个整数Cij(0≤Cij≤1e5)。第i+1行的第j个整数表示从第i个杯子第j个杯子倒水所需要付出的代价。保证Cii等于0。

输出格式
输出一个整数。表示Mislav需要付出的总代价的最小值。

样例
样例输入1
3 3
0 1 1
1 0 1
1 1 0
样例输出1
0
样例输入2
3 2
0 1 1
1 0 1
1 1 0
样例输出2
1
样例输入3
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
样例输出3
5
数据范围与提示
对于40%的数据,N≤10。

这道题考场上我想到的居然是最小生成树(于是就成功的爆零了),如果加了特判的话就有80分,主要还是数据太水了吧。

下面就来讲讲正解呗,因为之前接触过状压DP,所以做起来还是比较顺。

一个二进制数\(0000\)表示4个水杯都装有水,\(0001\)表示第一个水杯是空的,代价最小的状态,同理,\(1000\)表示第4个水杯是空的,代价最小的状态。

现在来考虑状态转移,拿\(1000\)举例,不难发现它可以转移到。

  • \(1100\)
  • \(1010\)
  • \(1001\)
    (可以这么考虑,我们在\(1000\)这个状态下进行了一个将\(i\)杯子的水倒入了\(j\)的操作。不管\(i\)\(j\)是多少,总会有一个杯子会空掉,所以会多出一个"1"出来)

因为每次操作只能在两个装有水的杯子\(i\)\(j\)的杯子进行,所以我们只需要在当前状态\(1000\)里暴力枚举位数为\(0\)\(i\)\(j\),模拟将\(i\)倒入\(j\)的操作。因为\(i\)倒入了\(j\),所以第\(i\)位为1,权值为\(w_{ij}\)

那么我们便可以得出状态转移方程:
\(f_{S+1<<i} = min(f_{S}+w_{ij})\)

S为当前状态,一个二进制数。当空的杯子,也就是S的1的个数为\(n-k\)时,我们便可以记录答案。

代码

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

#define N 30

int n,k,w[N][N],ans,inf,f[1<<21];

int main() {
    memset(f,0x3f,sizeof(f));
    ans=f[0];
    cin>>n>>k;
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
        cin>>w[i][j];
    f[0]=0;
    for(int S=0;S<1<<n;S++) {
        int sum=0;
        for(int i=1;i<=n;i++) if(S & (1<<(i-1))) sum++;
        if(sum==n-k) {
            ans=min(ans,f[S]);
            continue;
        }
        if(sum>n-k) continue;
        for(int i=1;i<=n;i++)
            if(!(S&(1<<(i-1))))
                for(int j=1;j<=n;j++)
                    if(!(S & (1<<(j-1))) && i!=j) 
                        f[S+(1<<(i-1))]=min(f[S+(1<<(i-1))],f[S]+w[i][j]);
    }
    cout<<ans;
}
网友评论