1. 程式人生 > >codeforces 1070J 2018-2019 ICPC,NEERC,Southern Subregional Contest Streets and Avenues in Berhattan

codeforces 1070J 2018-2019 ICPC,NEERC,Southern Subregional Contest Streets and Avenues in Berhattan

題面

題意

m m 條橫線, n n 條豎線,共有 ( n

m ) (n*m) 個交叉點, k ( k > =
m + n ) k(k>=m+n)
個名字(每個名字是一個大寫字母),現在要給這 ( m +
n ) (m+n)
條線命名,若構成一個交叉點的兩條直線的名字相同,則這個交叉點不美麗,問不美麗的交叉點的最少個數是多少。

做法

首先任意一條橫線與任意一條豎線都會有一個交點,因此答案僅與哪些名字給了橫線,哪些名字給了豎線有關,與每個名字具體給了哪條線無關。
而且只有當一種名字同時給了橫線和豎線後才會產生不美麗的交叉點,個數為兩邊數量之積。
不難發現最優解中只有一種名字同時給了橫線和豎線。
因此,我們可以暴力列舉那種既命名豎線又命名橫線的名字 t t ,為了讓這種名字出現的次數儘可能少,應儘可能多的用其他名字命名,所以其他同種名字必須全部命名橫線或全部命名豎線(除非名字數量過多),因此可以01揹包來求是否可以選擇一些種類的名字,使其總個數為 i ( i < = n ) i(i<=n) ,然後用這 i i 個名字用於命名橫線,除了 t t 之外的 min ( k c n t [ t ] i , m ) \min(k-cnt[t]-i,m) 個名字命名豎線。
據此,我們可以暴力列舉名字 t t 用於命名橫線的個數 i i ,則這種名字用於命名豎線的個數為 max ( c n t [ t ] i ( k m n ) , 0 ) \max(cnt[t]-i-(k-m-n),0) a n s = min ( a n s , i max ( c n t [ t ] i ( k m n ) , 0 ) ) ans=\min(ans,i*\max(cnt[t]-i-(k-m-n),0)) ,這種情況合法當且僅當:用其他名字可以表示出 ( m i ) (m-i) 個名字(同種名字必須同時使用,用01揹包判斷)。

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N 200100
using namespace std;

ll T,m,n,K,dy,cnt[30],ans;
char str[N];
bool dp[N]={1};

int main()
{
	ll i,j,k;
	cin>>T;
	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) swap(m,n);
		for(i=1;i<=K;i++) 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));
			}
		}
		printf("%lld\n",ans);
	}
}