题目链接:http://codeforces.com/contest/793/problem/D
题意:给出n个点m条边选择k个点,要求k个点是联通的而且不成环,而且选的边不能包含选过的边不能包含以前
选过的点,问最小的权值是多少。
题解:像这种取最小的一般可以考虑一下dp,然后再看一下题目,由于每次选的边都不能包括以前选的点,所以每
选择一条边能选择的区间范围就缩小了。
设dp[pos][l][r][k](这里可能会有人觉得开80*80*80*80会不会有点大了,自行计算一下不会爆内存的),pos表示当前位置,l表示区间左端点,r表示区间右端点,k表示还剩下多少条边没选当然也可以
设选了多少条边。附上记忆化搜索代码
int dfs(int pos , int l , int r , int count) {
if(dp[pos][l][r][count] != -1) {
return dp[pos][l][r][count];
}
if(count == 1) {
dp[pos][l][r][count] = 0;
return 0;
}
int sum = inf;
for(int i = 1 ; i <= n ; i++) {
if(mmp[pos][i] != inf) {
if(i > l && i < r) {
int sta = l , end = r;
if(i < pos) {
end = pos;
}
else {
sta = pos;
}
sum = min(sum , dfs(i , sta , end , count - 1) + mmp[pos][i]);
}
}
}
dp[pos][l][r][count] = sum;
return sum;
}
#include <iostream> #include <cstring> #include <cstdio> #define inf 0X3f3f3f3f using namespace std; const int M = 82; int n , k , m , u , v , c; int mmp[M][M] , dp[M][M][M][M]; int dfs(int pos , int l , int r , int count) { if(dp[pos][l][r][count] != -1) { return dp[pos][l][r][count]; } if(count == 1) { dp[pos][l][r][count] = 0; return 0; } int sum = inf; for(int i = 1 ; i <= n ; i++) { if(mmp[pos][i] != inf) { if(i > l && i < r) { int sta = l , end = r; if(i < pos) { end = pos; } else { sta = pos; } sum = min(sum , dfs(i , sta , end , count - 1) + mmp[pos][i]); } } } dp[pos][l][r][count] = sum; return sum; } int main() { scanf("%d%d" , &n , &k); scanf("%d" , &m); memset(mmp , inf , sizeof(mmp)); for(int i = 0 ; i < m ; i++) { scanf("%d%d%d" , &u , &v , &c); mmp[u][v] = min(mmp[u][v] , c); } memset(dp , -1 , sizeof(dp)); int ans = inf; for(int i = 1 ; i <= n ; i++) { ans = min(ans , dfs(i , 0 , n + 1 , k)); } if(ans == inf) { printf("-1 "); } else { printf("%d " , ans); } return 0; }