B: 导游2
时间限制: 1 Sec 内存限制: 128 MB
题目描述
宁波市的中小学生们在镇海中学参加程序设计比赛之余,热情的主办方邀请同学们参观镇海中学内的各处景点,已知镇海中学内共有n处景点。现在有n位该校的学生志愿承担导游和讲解任务。每个学生志愿者对各个景点的熟悉程度是不同的,如何将n位导游分配至n处景点,使得总的熟悉程度最大呢?要求每个景点处都有一个学生导游。
输入
有若干行:
第一行只有一个正整数n,表示有n个景点和n个学生导游。
第二行至第n+1行共n行,每行有n个以空格分隔的正整数。第i+1行的第j个数k(1≤k≤1000),表示第i个学生导游对景点j的熟悉程度为k。
输出
只有一行,该行只有一个正整数,表示求得的熟悉程度之和的最大值。
样例输入
3
10 6 8
9 2 3
1 7 2
样例输出
24
提示
【样例说明】
第1个学生负责第3个景点,第2个学生负责第1个景点,第3个学生负责第2个景点时,熟悉程度总和为24,达到最大值。
【数据限制】
50%的数据,1≤n≤9;100%的数据,1≤n≤17。
Solution
很显然一道状压DP题目
问题凑向抽象出来就是:
求N个人恰好去N个景点的最大熟悉度
显然满足最有子结构的性质:即一个人选择去k号景点时,最优解包含n-1个人去n-1个景点的最大熟悉度,这也就给出了分解方案:
枚举第n人去k号景点,再加上n-1个人去n-1个景点的最大熟悉度即可,即:
dp[i][sta]表示前i个人去sta状态表示的景点时的最大熟悉度
所以:
dp[i][sta]=max{dp[i-1][sta-(1<<(k-1))]+a[i][k]} (sta&(1<<(k-1))>0)
空间上的小优化:
操作时可以把第一维省掉,每次统计一下sta中1的个数即可
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=17;
int a[maxn+1][maxn+1];
int dp[1<<maxn];
int n,num;
int main()
{
cin>>n;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
cin>>a[i][j];
memset(dp,0,sizeof(dp));
for (int sta=1;sta<=(1<<n)-1;sta++)
{
num=0;
for (int j=0;j<n;j++)
if ((sta&(1<<j))>0) num++;
for (int i=1;i<=n;i++)
if (sta&(1<<(i-1)))
dp[sta]=max(dp[sta],dp[sta-(sta&(1<<(i-1)))]+a[num][i]);
}
cout<<dp[(1<<n)-1];
return 0;
}