zoukankan      html  css  js  c++  java
  • 模拟赛毒瘤状压DP题:Kronican

    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;
    }
    
  • 相关阅读:
    哲学家进餐
    文件系统
    文件读写原理(转)
    数据库join种类
    http与https区别
    数字证书(转)
    B. Rebranding
    扩展欧几里德算法、证明及其应用
    CodeForces 7C Line
    UVALive 7147 World Cup
  • 原文地址:https://www.cnblogs.com/MisakaMKT/p/11252278.html
Copyright © 2011-2022 走看看