题
比特山是一个旅游胜地,它一共有 n 个景点,按照海拔高度从低到高依次编号为 1 到 n。为了更好地帮助游客们欣赏这里的风景,人们在上面搭建了 m 条缆车路线。每条缆车路线只可能把游客们从某个海拔较低的景点运送到另一个海拔较高的景点。
在每个景点都有一家纪念品连锁商店,其中第 i 个景点的商店隶属第 ci号公司,两家连锁店 (i,j)(i,j) 隶属同一公司当且仅当 ci=cj。每家公司都有新客优惠活动,其中第ii家公司对于新客的优惠红包为 wi 元,一旦领取了隶属该公司的某家连锁店的一份红包,就不能再领取该公司所有分店的红包。
你正在 1 号景点,你将会搭乘缆车去往各个景点,每到一个景点,你都可以领取该景点的连锁商店的新客优惠红包(包括 1号景点)。当然,同一家公司的红包最多只能领一次。请写一个程序,对于每个可能的终点 k,找到一条从 1 号景点出发到达 k 号景点的游览路线,使得可以领取到总金额最多的优惠红包。
Input
第一行包含两个正整数 n,m (2≤n≤36, 1≤m≤ n(n−1)/2),分别表示景点的数量以及缆车路线的数量。
第二行包含 n 个正整数 c1,c2,…,cn (1≤ci≤n),依次表示每个景点的商店所隶属的公司。
第三行包含 n 个正整数 w1,w2,…,wn (1≤wi≤106)依次表示每家公司的新客优惠红包的金额。
接下来 m 行,每行两个正整数 ui,vi(1<ui,vi≤n),表示一条缆车路线,起点是景点 ui,终点是景点vi。输入数据保证任意两个景点之间最多只有一条缆车路线,且从 1 号景点出发可以到达任意一个景点。
Output
输出 n 行,第 i 行输出一个整数,即从 1 号景点出发到达 i 号景点时,领取的优惠红包的总金额的最大值。
Sample 1
Input
5 5
1 2 2 3 4
1 4 5 9 3
1 2
2 3
3 5
1 4
4 5
Output
1
5
5
6
15
代码
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
int n,m;
ll c[50],w[50];
ll sum[50];
ll p[50];//p[1]=10100 景点1可以直接到达 景点3(二进制从右向左第3个为1) 或 景点5 (二进制从右向左第3个为1)
// 同理 p[3]=111000 景点3可以直接到达 景点4 景点5 景点6
ll b[50];//标记
ll dp[50];
void dfs(int now,int fron)
{
if(b[c[now]]==0)//同一家公司的红包最多只能领一次
dp[now]=dp[fron]+w[c[now]];
else
dp[now]=dp[fron];
b[c[now]]++;
sum[now]=max(sum[now],dp[now]);
for(int i=now+1;i<=n;i++)
{
if(p[now]&((ll)1<<i))
{
dfs(i,now);
b[c[i]]--;
}
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&c[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&w[i]);
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d %d",&a,&b);
p[a]=p[a]+((ll)1<<b);//也可以写为 p[a]=p[a]|((ll)1<<b);
// 看上第7行 数组p的解释
// 注意 ((ll)1<<b) 写成 ((ll)(1<<b)) 就不对了,找bug找了好久 ┭┮﹏┭
}
for(int i=1;i<=n;i++)//出发点(不一定是起点)
for(int j=i+2;j<=n;j++)//目的地
for(int k=i+1;k<j;k++)//经过点
{
if((p[i]&((ll)1<<j))&&(p[i]&((ll)1<<k))&&(p[k]&((ll)1<<j)))
p[i]-=(ll)1<<j;
}
//就是讲 景点i可以直接到达景点j:i ---> j
//但是还有一种情况景点i可以间接到达景点j,如下
// 景点i可以直接到达景点k,景点k可以直接到达景点j: i ---> k ---> j
//根据题意 可以领取到总金额最多的优惠红包 故选择 i ---> k ---> j ,防止超时 就把 i ---> j 路线删除
dfs(1,0);
for(int i=1;i<=n;i++)
printf("%lld\n",sum[i]);
}