1 题目大意
有mmm条横线,nnn条竖线,共有 (n∗m)(n*m)(n∗m) 个交叉点.
(m+n≤k)(m+n≤k)(m+n≤k)个名字(每个名字是一个大写字母),现在要给这(m+n)(m+n)(m+n)条线命名,若构成一个交叉点的两条直线的名字相同,则这个交叉点不美丽。
问不美丽的交叉点的最少个数是多少。
多组数据,第一行t(数据组数)(1≤t≤30000)(1≤t≤30000)(1≤t≤30000)
每组数据依次为n,m,kn,m,kn,m,k,第二行是名称.
(1≤n,m≤30000;n+m≤k≤2∗105)(1≤n,m≤30000;n+m≤k≤2*10^5)(1≤n,m≤30000;n+m≤k≤2∗105)
2 分析
我们对于一种名称,要尽可能将它全部置于横线或者全部置于竖线上,对于样例1的第二个数据来说,最优方案就是把2条竖线全部置为E,横线中两个E,五个Z。这样看来,我们只需要关注名称分配到横线上还是竖线上了,不必考虑分配到哪个线上。
我们的目标就变成分配颜色到横线和竖线中(而不是哪根横线和哪根竖线中),使得横竖同名数尽量少(因为ansansans=横向同名*纵向同名)。
再举一个例子:
| E | F | |
|---|---|---|
| E | + | |
| E | + | |
| E | + | |
| E | + | |
| E | + | |
| E | + | |
| F | + |
这样填空肯定不是最优的,因为有两种名字都重复了,我们可以将行中的FFF和列中的EEE互换,那么就可以达到更优情况。所以我们又有了一个结论:当重名种类大于等于222时,一定不是最优解。
那么接下来就很清晰了,我们枚举复的名字种类,去看哪种重复更好。然后我们考虑iii为重复的颜色,jjj为它在列中存在的个数,那么它在行中会有(cnt[i]−j−(k−n−m)(cnt[i]-j-(k-n-m)(cnt[i]−j−(k−n−m)个(cnt[i]cnt[i]cnt[i]表示字母iii的个数)。
ans=cnt[i]−j−(k−n−m)ans=cnt[i]-j-(k-n-m)ans=cnt[i]−j−(k−n−m)
这个结论成立,当且仅当其他的字母可以填满列中剩余的(n−j)(n-j)(n−j)个空。
我们只需要在更新ansansans之前判断其他的字母可以表示的位置就行了。
3 代码
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N 200100
#define maxx 27
using namespace std;
ll T,m,n,K,dy,cnt[maxx],ans;
char str[N];
bool dp[N];
int main(){
ll i,j,k;
scanf("%lld",&T);
dp[0]=1;
while(T--){
memset(cnt,0,sizeof cnt);
ans=INF;
scanf("%lld%lld%lld%s",&m,&n,&K,&str[1]);
dy=K-m-n;
if(m>n){//保证n是大的
swap(m,n);
}
for(i=1;i<=K;i++){//出理cnt数组
cnt[str[i]-'A'+1]++;
}
for(i=1;i<=26;i++){
for(j=1;j<=n;j++){//清空
dp[j]=0;
}
for(j=1;j<=26;j++){
if(i==j){//要是不同种颜色
continue;
}
for(k=n;k>=cnt[j];k--){//处理能填满的
dp[k]|=dp[k-cnt[j]];
}
}
for(j=min(cnt[i],n);j>=0;j--){
if(!dp[n-j]){//如果其他颜色不能填到这
continue;
}
ans=min(ans,j*max(0ll,cnt[i]-j-dy));//更新ans
}
}
printf("%lld\n",ans);
}
return 0;
}
本文探讨了一种算法,旨在解决给定横线、竖线和名称约束下,如何分配名称以最小化由同名直线构成的交叉点数量的问题。通过优化颜色分配策略,避免在横线和竖线上出现重复名称,从而减少不美丽交叉点的产生。

497

被折叠的 条评论
为什么被折叠?



