题目描述
斌斌翻水水的家乡有很多个水井,每个水井 i 和其他水井 j 都有一个管子连通,这样一个水井没水的时候其他的水井可以通过水管直接输送水过来,而这个管子的容量是vij,
有一天斌斌翻水水在野外烧烤的时候被一个奇异的小石头砸中,当时斌斌翻水水就觉的这个石头很特别就带回了家,在多年的研究之下他发现这个石头有个神奇的功能:
它能够将多个水井划分成两个区域A和B,在同一个区域的水井之间不再需要水管连接,神奇的小石头可以直接让他们的水流通,但是在不同区域的水井却不行,仍然需要水管相连接。
本来正常人直接把所有水井都放到A或者B,这样所有水井都互通了,但是斌斌翻水水不一样,他觉的这些水管放着不用可惜,他想让水井之间用到的水管的容量总数最大(每一个水井不是在A区域就是在B区域
不能独立在外),但是斌斌翻水水又很懒不想自己去想怎么进行划分,所以他把这个难题抛给了聪明的你,希望你能帮住到他。
输入
输入一共N+1行,第一行为一个数字N(2 <= N <= 20),代表一共有几个水井,接下去有N行数字,每行数字有N个数字,第i行数字的第j列数字代表Vij.(注意 vij = vji , vii = 0, 0 <= vij <= 10000)(例如样例中的 0 50 30 的 50代表第一个水井和第二个水井之间的水管容量为50)
输出
一共一行,那一行包含一个数字,代表斌斌翻水水让你求的利用水管的最大容量总和。
样例输入
3
0 50 30
50 0 40
30 40 0
样例输出
90
提示
90的答案是这样来的,将第一个水井和第三个水井放在A,将第二个水井放到B,那么第一个水井和第三个水井和第二个水井之间的水管要连接那么便是50+40 = 90。
虽然神奇的石头拥有着超自然的力量,但是其他物质和现实生活中一样,所以你无需考虑水管容量是负数的情况,只需要考虑水管是非负数的情况就行了。
解题思路:
这个题比较考语文, vij表示的是代表管子容量的二维数组, 例如井1和井2之间管子的容量就是v[1][2]。在将井分配到A,B俩个区域之后, 他们之间的管子容量也就确定了下来。
例如A区域有井1和井2,B区域有着井3和井4,那么此时的容量就为 v[1][3] + v[1][4] + v[2][3] + v[2][4]。
输入中的第2行至第N+1行所输入的就是V这个代表管子容量的二维数组。
看懂了题目之后,我们来思考这个题该怎么写:
首先我们会想到使用dfs, 将所有情况都考虑到然后选出容量最大的那个。但是我第一遍这么做的时候失败了, 当时我的dfs仅仅只有一个参数, 就是我所遍历的井, 所以每执行一次都要用一个二重循环来算出当时的容量, 结果就是超时了。 所以这次的dfs将引入俩个变量, 第一个代表我遍历的井, 第二个代表之前的状态(之前有多少容量), 这样我们就可以减少时间复杂度。
大概做法就是:一开始就将所有的井都放在一个区域, 然后一个一个的往另一边拿一个井。废话不多说,现在上代码:
#include<iostream>
#include<algorithm>//dfs里用到了max函数
using namespace std;
int n;
int pipe[40][40];
int vis[30];
int ans;
void dfs(int x, int total)//total表示的是之前的总容量。
{
if(x==n+1) return ;
ans = max(ans, total);//如果找到更大的了,就更新ans。
int num = total;//因为total这个状态还有用,可以换着水井往左边拿,引入num更加方便。
vis[x] = 1;//拿到左边的水井需要标记。
for(int i=1;i<=n;i++){
if(vis[i]==0){
num += pipe[x][i];//新拿到左边的水井会增加和右边水井的容量。
}else {
num -= pipe[x][i];//新拿到左边的水井和原本就在左边的水井之间的容量会消失,所以要剪掉。
}
}
dfs(x+1, num);//开始继续从右边拿水井。
vis[x]=0;
dfs(x+1, total);//把拿过去的拿回来,换一个拿过去。
}
int main()
{
scanf("%d", &n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d", &pipe[i][j]);
}
}
dfs(1,0);
printf("%d", ans);
return 0;
}