“浪潮杯”第九屆山東省ACM大學生程式設計競賽重現賽 題解
點選轉到(牛客網)
1.思路:
給定兩個字串,假設是ELLY與KRIS,E到K是6,L到R是6,當第二個L到I時,L是比I大的,此時L就要繞到Z,從Z到A,再從A開始到I,這樣長度就是23,Y到S同理,長度是20;這樣找完之後序列長度之和就是6 +6+23+20=55.這是題目中給出的一種解答。但是題目要求我們找字元間最小的長度,我就把第一個字串中的每一個字元與第二個字串中的每一個字元比較,每次都找出最短的長度,然後加在一起即可。
舉例:ELLY與KRIS,第一個字串中的第一個字元E與第二個字串中的每一個字元比較,找出最少的長度並記錄下來;再從第一個字串中的第二個字元L與第二個字串中的每一個字元比較,依舊找最小的,就這樣依次迴圈,把長度累加即可。
2.程式碼:
#include<bits/stdc++.h> using namespace std; const int maxn=1<<30; char str1[55],str2[55]; char s1[55],s2[55]; int vis[100]; int main() { while(scanf("%s%s",str1,str2)!=EOF) { memset(vis,0,sizeof(vis)); int sum=0; int len1=strlen(str1); int len2=strlen(str2); for(int i=0;i<len1;i++) s1[i]=str1[i]; for(int i=0;i<len2;i++) s2[i]=str2[i]; for(int i=0;i<len1;i++) { int minn=maxn; int temp; for(int j=0;j<len2;j++) { if(vis[j]==0) { if((s1[i]-'0')-(s2[j]-'0')>0) { if(abs((s1[i]-'0')-(s2[j]-'0')-26)<minn) { minn=abs((s1[i]-'0')-(s2[j]-'0')-26); temp=j; } } else { if(abs((s1[i]-'0')-(s2[j]-'0'))<minn) { minn=abs((s1[i]-'0')-(s2[j]-'0')); temp=j; } } } } vis[temp]=1; sum=sum+minn; } printf("%d\n",sum); } return 0; }
1.題目含義:
在GGO,一個以槍支和鋼鐵為主導的世界,球員們正在爭取成為最強大的槍手。玩家Shino是一個狙擊手,她的目標射擊一次殺死一個怪物。現在她在n * n地圖中,並且在某些網格中有怪物。每個怪物都有經驗。然而,作為一個大師,詩乃有一種奇怪的自我約束。她最多會殺死一列中的一個怪物,最多也會殺死一行中的一個怪物。現在,她希望知道如何在殺死儘可能多的怪物的前提下獲得最大的體驗。
2.思路:
明顯來看,是二分,求最小最大問題,把消滅的怪獸的數量作為一個衡量的標準,對此二分圖求出最大匹配ans。那麼ans為最多能消滅的怪物數量。然後二分列舉經驗值k,對於所有矩陣內值
3.程式碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn=550;
int n;
int from[maxn],w[maxn][maxn],ans,vis[maxn];
vector<int> g[maxn];
bool Find(int u)
{
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(!vis[v])
{
vis[v]=1;
if(from[v]==-1 || Find(from[v]))
{
from[v]=u;
return true;
}
}
}
return false;
}
int match()//求出最大能射死怪獸的數量
{
int ret=0;
memset(from,-1,sizeof(from));
for(int i=0;i<n;i++)
{
memset(vis,0,sizeof(vis));
if(Find(i) ) ret++;
}
return ret;
}
bool check(int k)
{
for(int i=0;i<n;i++)
g[i].clear();
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(w[i][j]>=k)
g[i].push_back(j);
return match()==ans;
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)
g[i].clear();
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&w[i][j]);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(w[i][j])
{
g[i].push_back(j);
}
}
ans=match();
int L=0,R=1e9;
while(L<R)
{
int mid=(L+R+1)>>1;
if(check(mid))
L=mid;
else
R=mid-1;
}
printf("%d\n",L);
}
return 0;
}
1.題意:
Byteland有n個城市,i城市有價值a。 在兩個城市之間建立雙向道路的成本是它們的價值的總和。 請計算連線這些城市的最低成本,這意味著任何兩個城市都可以相互聯絡.
2.思路:
貪心演算法:想要將任意兩個城市連線且道路花費之和最小,那可使每條道路的花費最少,道路的花費等於兩端城市value之和,由此可知,只要每個城市與最小value的那個城市相連通,所得的花費必定是最小的。
因此,將最小value的城市放於中間與其他城市一一相連。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define p 100001
using namespace std;
int main(){
int t;
int a[p];
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
if(n==1){
printf("0\n");
continue;
}
sort(a,a+n);
int minn=a[0];
long long sum=0;
for(int i=1;i<n;i++){
sum=sum+a[i]+minn;
}
printf("%lld\n",sum);
}
}
1.題意:
一個樹形結構(根節點是0),給定每個節點的父節點的編號,手軸(hand scroll)個數,從該節點到父節點完成一次升級釋放的能量。
升級規則如下:必須從最低層開始,逐層升級,從底層到上一級需消耗一個手軸(hand scroll)才能完成升級,直到升級到最高層,這個時候升級的總能量。
2.
其實,題意明確了之後,很容易看到滿足要求的路徑總共有三條(從葉節點到根),通過這三條路徑完成一次升級所釋放的能量也是固定的,到底先讓哪條先完成升級呢?
顯然,每條路徑到底能夠升級多少次,受手軸(hand scroll)個數的制約。要想讓能量最高,那就先讓能量高的路徑優先完成升級。
3.程式碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int n,fa[maxn],pre[maxn],num[maxn],power[maxn];
vector<int> son[maxn];
int value[maxn],leaf[maxn],cnt;
/*
dfs()時間複雜度分析:
對於具有n個頂點、e條邊的圖來說,dfs演算法對圖中的每個頂點最多呼叫一次,因此其遞迴呼叫總次數為n。當訪問某個
頂點v時,dfs的時間主要花在從該頂點出發查詢它的鄰接點上。當用鄰接表表示圖時,需要遍歷該頂點的所有鄰接點,所有dfs
的總時間為O(n+e);當用鄰接矩陣表示圖時,需要遍歷該頂點行的所有元素,所以dfs的總時間為O(n^2).
*/
//對於樹來說,e=n-1。用鄰接表儲存樹,所以,dfs時間複雜度為O(n)
void dfs(int id,int w){ //找到葉節點以及每條路徑完成每次升級獲得到的能量
value[id]=w;
if(son[id].size()==0){
leaf[cnt++]=id;
return ;
}
int len=son[id].size();
for(int i=0;i<len;i++){
int x=son[id][i];
//printf("power[%d]=%d\n",x,power[x]);
dfs(x,w+power[x]);
}
}
bool cmp(int a,int b){
return value[a]>value[b];
}
int previs(int x,int w){
if(x==0) return w;
if(num[x]==0) return 0;
if(num[x]<w){w=num[x];num[x]=0;}
else num[x]-=w;
return previs(fa[x],w);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&fa[i],&num[i],&power[i]);
son[fa[i]].push_back(i);
}
dfs(0,0);
/*for(int i=0;i<cnt;i++){
printf("%d %d\n",leaf[i],value[leaf[i]]);
}*/
sort(leaf,leaf+cnt,cmp);
ll ans=0;
int min_num;
for(int i=0;i<cnt;i++){ //cnt條路徑,由路徑value[]從大到小進行求能量值
int x=leaf[i];
min_num=previs(x,num[x]); //注意:這裡求每段路徑的最小手軸數用遞迴來求,遞推的話會超時
ans+=1ll*min_num*value[x];
}
/*
//超時
for(int i=0;i<cnt;i++){ //cnt條路徑,由路徑value[]從大到小進行求能量值
int x=leaf[i];
min_num=num[x];
int y=fa[x];
while(y){
min_num=min(min_num,num[y]);
y=fa[y];
}
ans+=1ll*min_num*value[x];
if(min_num){
y=x;
while(y){
num[y]-=min_num;
y=fa[y];
}
}
}
*/
printf("%lld\n",ans);
return 0;
}
1.題目含義:
存在j< i且a[j]< a[i],首先存在一個good序列,讓我們從1到n個元素中,移除一個元素,使得good數最大化。good數的定義就是題目中的定義:j< i且a[j]< a[i],只要滿足這樣一個條件,就存在good數。為了判斷刪除序列中的哪一個元素使得good數最大化,就要對每一個數字做判斷,用cnt[x]函式代表,刪除x後,會減少的good數,找出使得減少的good數最少的元素,刪除即可。如果存在多個相同對的good數,就刪除最小的那個good數。
2.程式碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000005;
int cnt[maxn];
int main()
{
int t;
while(scanf("%d",&t)!=EOF)
{
while(t--)
{
int n;
scanf("%d",&n);
int min1=maxn,min2=maxn;
for(int i=0;i<n;i++)
{
int x;
scanf("%d",&x);
cnt[x]=0;
if(min1<x && x<min2)
{
//此時min1<x<min2, min1的存在使得x成為good數,刪除min1,x就不是good數
//所以刪除min1之後,good數會減1
cnt[min1]++;
}
if(min1<x)
{
//min1<x,x本身就是一個good數,刪除x本身之後,good數就會減1
cnt[x]++;
}
if(x<min1)
{
//此時更新min1與min2的值,使得min1與min2的值為x元素之前的最小值與次最小值
min2=min1;
min1=x;
}
else
{
if(x<min2)
{
//更新min2的值,使得min2的更新為x元素之前的次最小值
min2=x;
}
}
}
int mini=maxn,ans;
for(int i=1;i<=n;i++)
{
if(cnt[i]==mini)//如果有多個good數,那麼就選最小的那一個
{
ans=min(ans,i);
}
else if(cnt[i]<mini)
mini=cnt[i],ans=i;//找使得good數減少的最少的數,這個數就是要移除對的數
}
printf("%d\n",ans);
}
}
return 0;
}
1.題意:
給四個區間,要求每個區間選一個數字組成一個四元組(x1,x2,x3,x4),要求是x1!=x2,x2!=x3,x3!=x4,x4!=x1。
每一個區間的長度代表區間中數字的個數,從每一個區間中取出一個數字組成一個四元組,就像數學中的組合一樣,組合的總數就是四個區間長度的乘積,但是題目中明確了不合法的條件,所以用總數再減去x1==x2,x2==x3,x3==x4,x4==x1這四種兩兩相交的情況,再加回所有三個集合相交的部分,再減去所有四個集合相交的部分。
2.題解:
用到四個集合的容斥定理:
|A∪B∪C∪D|=|A|+|B|+|C|+|D|-|A∩B|-|A∩C|-|A∩D|- |B∩C| - |B∩D| - |C∩D|+|A∩B∩C|+|A∩B∩D|+|A∩C∩D|+|B∩C∩D| -|A∩B∩C∩D|
3.程式碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll mod=1e9+7;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ll l1,r1,l2,r2,l3,r3,l4,r4;
ll left, right,accl,accr;
scanf("%lld%lld%lld%lld%lld%lld%lld%lld",&l1,&r1,&l2,&r2,&l3,&r3,&l4,&r4);
ll ans=(r1-l1+1)%mod*(r2-l2+1)%mod*(r3-l3+1)%mod*(r4-l4+1)%mod;
//x1==x2
left=max(l1,l2);
right=min(r1,r2);
if(left<=right)
ans=((ans-(right-left+1)*(r3-l3+1)%mod*(r4-l4+1)%mod)%mod+mod)%mod;
//x2==x3
left=max(l2,l3);
right=min(r2,r3);
if(left<=right)
ans=((ans-(r1-l1+1)*(right-left+1)%mod*(r4-l4+1)%mod)%mod+mod)%mod;
//x3==x4
left=max(l3,l4);
right=min(r3,r4);
if(left<=right)
ans=((ans-(r1-l1+1)*(r2-l2+1)%mod*(right-left+1)%mod)%mod+mod)%mod;
//x4==x1
left=max(l1,l4);
right=min(r1,r4);
if(left<=right)
ans=((ans-(right-left+1)*(r2-l2+1)%mod*(r3-l3+1)%mod)%mod+mod)%mod;
//x1==x2 && x2==x3
left=max(l1,max(l2,l3));
right=min(r1,min(r2,r3));
if(left<=right)
ans=(ans+(right-left+1)*(r4-l4+1)%mod)%mod;
//x1==x2 && x2==x4
left=max(l1,max(l2,l4));
right=min(r1,min(r2,r4));
if(left<=right)
ans=(ans+(right-left+1)*(r3-l3+1)%mod)%mod;
//x1==x2 && x3==x4
left=max(l1,l2);
right=min(r1,r2);
accl=max(l3,l4);
accr=min(r3,r4);
if(left<=right && accl<=accr)
ans=(ans+(right-left+1)*(accr-accl+1)%mod)%mod;
//x2==x3 && x3==x4
left=max(l2,max(l3,l4));
right=min(r2,min(r3,r4));
if(left<=right)
ans=(ans+(r1-l1+1)*(right-left+1)%mod)%mod;
//x2==x3 && x1==x4
left=max(l2,l3);
right=min(r2,r3);
accl=max(l1,l4);
accr=min(r1,r4);
if(left<=right && accl<=accr)
ans=(ans+(right-left+1)*(accr-accl+1)%mod)%mod;
//x3==x4 && x1==x4
left=max(l1,max(l3,l4));
right=min(r1,min(r3,r4));
if(left<=right)
ans=(ans+(right-left+1)*(r2-l2+1)%mod)%mod;
//x1==x2 && x2==x3 && x3==x4
left=max(max(l1,l2),max(l3,l4));
right=min(min(r1,r2),min(r3,r4));
if(left<=right)
ans=((ans-(right-left+1)*3)%mod+mod)%mod; //注意減去的是3倍的x1==x2 && x2==x3 && x3==x4。
printf("%lld\n",ans);
}
return 0;
}
特別感謝各位小可愛們,大家一起整理,真好。呀呀呀(斜眼笑)!!!