tyvj 1348 清理内奸

[size=large][align=center]清理内奸[/align][/size]

描述 Description
最近FF博士在玩一个游戏“三国杀杀杀”,他扮演一个英明的主公,四处清剿反贼。终于把反贼全部干掉了,可是他的手下还存在着一些内奸

。经过他的观察和研究,终于发现了内奸的重要特征。内奸最重要的工作就是传递情报,所以内奸必须掌握各种加密方法。于是他们对素数有

一种偏好,编号是素数的人全部是内奸。FF博士将指挥忠臣干掉内奸。忠臣和内奸排成一排,第i个人的编号为Bi。杀死内奸会获得一定量经验

,等级为X的忠臣杀死任意等级的内奸会获得X点经验。每个人都有一定的攻击范围,若站在i位置的人攻击范围为k那么他可以杀[i-k,i+k]范围

内的人,但是每个人都只有一次攻击的机会。内奸们并不知道自己的身份已经暴露,不会攻击任何人,只能做待宰的羔羊,FF博士确定了攻击

方案后会在瞬间下令让忠臣行动,内奸会被秒(也就是内奸死亡不会对原来攻击范围造成影响)。
请帮FF博士计算
1、他这次最多能干掉多少内奸呢?
2、在干掉内奸人数最多的情况下,他能获得最多多少经验?

输入格式 Input Format
第一行一个数字n,代表FF博士手下有n个人
第二行n个数,第i个数代表第i个人的编号Bi
3~n+2行,每行2个数x,k分别代表第i个人的等级和攻击范围。

输出格式 Output Format
第一行是FF博士最多能干掉的内奸数量
第二行是FF博士在干掉最多内奸的基础上能获得的最多经验数。

样例输入 Sample Input
7
1 2 3 4 5 6 8
1 1
3 1
4 1
5 2
10 5
1 1
0 100

样例输出 Sample Output
3
7

时间限制 Time Limitation
1S

注释 Hint
样例解释
第2、3、5个人是内奸 (第7个不是,他的编号为8)
1杀2,获得1点经验
4杀3,获得5点经验
6杀5,获得1点经验
数据范围
对于100%的数据
n<=1000
Bi<=10^7
x,k<=100
所有数据不会出现负数。

分析:“这题其实很容易看出来,n个人分成了2个阵营。每个忠臣与他攻击范围内的内奸连一条边。此题第一问就是求二分图的最大匹配。第

二问是个贪心,权值在点上而不是在边上,所以按等级排序,优先满足等级高的忠臣。因为他一旦匹配成功,就不会被换下来。由于最大匹配

数肯定是确定的,所以让等级高的优先匹配并不会影响结果。”即对于二分原图得到的两个图,若两个子图内的元素之间不存在边,那么确定

一个图,另外一个图中的元素任意改变顺序,都不会影响到最大匹配,所以第二问中只要优先满足等级高的忠臣就可以得到正解。

小结:二分图的最大匹配+贪心(排序应该不是问题)

#include <iostream>
#include <algorithm>
#include <memory.h>
using namespace std;
const int maxn = 1010;
struct person
{
int pno;//编号
int grade;//等级
int range;//攻击范围
bool isPrime;//编号是否为素数
int cno;
};
struct node
{
int index;//下标
int grade;
};
person p[maxn];
node x[maxn];
int n;
int white,black;
bool cmp(node a,node b)
{
return a.grade>b.grade;
}
bool isPrime(int k)
{
if(k==1) return false;
if(k==2) return true;
for(int i=2;i*i<=k;i++)
{
if(k%i==0) return false;
}
return true;
}
int adjl[maxn][maxn];
int matx[maxn];
int maty[maxn];
int used[maxn];
int match;
bool find(int k)
{
for(int i=1;i<=adjl[k][0];i++)
{
int j=adjl[k][i];
if(!used[j])
{
used[j]=1;
if(maty[j]==-1||find(maty[j]))
{
maty[j]=k;
matx[k]=j;
return true;
}
}
}
return false;
}
void hungary()
{
for(int i=0;i<white;i++)
{
if(matx[x[i].index]==-1)
{
memset(used,0,sizeof(used));
if(find(x[i].index))
{
match++;
//cout<<x[i].index<<endl;
}
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>p[i].pno;
if(isPrime(p[i].pno)) p[i].isPrime=true;
else p[i].isPrime=false;
}
for(int i=0;i<n;i++) cin>>p[i].grade>>p[i].range;
white=black=0;
for(int i=0;i<n;i++)
{
if(p[i].isPrime) p[i].cno=black++;
else
{
p[i].cno=white;
x[white].index=white;
x[white++].grade=p[i].grade;
}
}
memset(adjl,0,sizeof(adjl));
for(int i=0;i<n;i++)
{
if(!p[i].isPrime)
{
for(int j=max(0,i-p[i].range);j<=min(i+p[i].range,n-1);j++)
{
if(p[j].isPrime)
{
int r=++adjl[p[i].cno][0];
adjl[p[i].cno][r]=p[j].cno;
}
}
}
}
/*for(int i=0;i<white;i++)
{
cout<<i<<": ";
for(int j=1;j<=adjl[i][0];j++)
{
cout<<adjl[i][j]<<" ";
}
cout<<endl;
}*/
sort(x,x+white,cmp);
memset(matx,-1,sizeof(matx));
memset(maty,-1,sizeof(maty));
match=0;
hungary();
int sum=0;
for(int i=0;i<n;i++)
{
if(!p[i].isPrime)
{
if(matx[p[i].cno]!=-1) sum+=p[i].grade;
}
}
cout<<match<<endl;
cout<<sum<<endl;
//system("pause");
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值