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;
    }
    
  • 相关阅读:
    part11-1 Python图形界面编程(Python GUI库介绍、Tkinter 组件介绍、布局管理器、事件处理)
    part10-3 Python常见模块(正则表达式)
    Cyclic Nacklace HDU
    模拟题 Right turn SCU
    状态DP Doing Homework HDU
    Dp Milking Time POJ
    区间DP Treats for the Cows POJ
    DP Help Jimmy POJ
    Dales and Hills Gym
    Kids and Prizes Gym
  • 原文地址:https://www.cnblogs.com/MisakaMKT/p/11252278.html
Copyright © 2011-2022 走看看