1. 程式人生 > 實用技巧 >集訓模擬賽2

集訓模擬賽2

T1翻轉游戲

題目描述

  翻轉游戲是在一個 4×4的正方形上進行的,在正方形的 個格上每個格子都放著一個雙面的物件。每個物件的兩個面,一面是白色,另一面是黑色,每個物件要麼白色朝上,要麼黑色朝上,每一次你只能翻 個物件,從而由黑到白的改變這些物件上面的顏色,反之亦然。每一輪被選擇翻轉的物件遵循以下規則:

  • 1.從16個物件中任選一個。
  • 2.翻轉所選擇的物件的同時,與它相鄰的左右上下四個物件(如果有的話)都要跟著翻轉。
  • bwbw
    wwww
    bbwb
    bwwb

  • 這裡b表示該格子放的物件黑色朝上,w表示該格子白色朝上。如果我們選擇翻轉第三行第一件物件,那麼格子狀態將變為

    bwbw
    bwww
    wwwb
    wwwb
    
  • 遊戲的目標是翻轉所有的物件白色朝上或黑色朝上。你的任務就是寫一個程式來求最少的的翻轉次數來實現這一目標。

輸入格式

  • 輸入檔案包含4行,每行4個字元,每個字元w或b表示遊戲開始時格子上物件的狀態。

輸出格式

  • 輸出檔案僅一個整數,即從給定狀態到實現這一任務的最少翻轉次數。如果不能實現就輸出:Impossible。如能實現就輸出結果

樣例

  • 輸入

    bwwb
    bbwb
    bwwb
    bwww
    
  • 輸出

    4
    

暴力列舉

 1 #include<bits/stdc++.h>
 2 using namespace std;
3 char s[5]; 4 int a[18],b[18]; 5 int sum,ans=1e7; 6 void change(int i){ 7 b[i]^=1; 8 sum++; 9 if(i>=5)b[i-4]^=1; 10 if(i<=12)b[i+4]^=1; 11 if(i%4!=1)b[i-1]^=1; 12 if(i%4!=0)b[i+1]^=1; 13 } 14 15 bool j(){ 16 int cnt=0; 17 for(int i=1;i<=16;i++){ 18 cnt+=b[i];
19 } 20 if(cnt==0||cnt==16)return 1; 21 else return 0; 22 } 23 24 int main(){ 25 for(int i=1;i<=4;i++){ 26 scanf("%s",s+1); 27 for(int j=1;j<=4;j++){ 28 if(s[j]=='b')a[(i-1)*4+j]=1; 29 } 30 } 31 int maxn=1<<16; 32 for(int s=0;s<maxn;s++){ 33 for(int i=1;i<=16;i++){ 34 b[i]=a[i]; 35 } 36 sum=0; 37 for(int i=1;i<=16;i++){ 38 if(s&(1<<(i-1))){ 39 change(i); 40 } 41 } 42 if(j())ans=min(ans,sum); 43 } 44 if(ans==1e7)printf("Impossible\n"); 45 else printf("%d\n",ans); 46 return 0; 47 }

T2搶掠計劃

題目描述:

Siruseri 城中的道路都是單向的。不同的道路由路口連線。按照法律的規定,在每個路口都設立了一個 Siruseri 銀行的 ATM 取款機。令人奇怪的是,Siruseri 的酒吧也都設在路口,雖然並不是每個路口都設有酒吧。

Banditji 計劃實施 Siruseri 有史以來最驚天動地的 ATM 搶劫。他將從市中心出發,沿著單向道路行駛,搶劫所有他途徑的 ATM 機,最終他將在一個酒吧慶祝他的勝利。

使用高超的黑客技術,他獲知了每個 ATM 機中可以掠取的現金數額。他希望你幫助他計算從市中心出發最後到達某個酒吧時最多能搶劫的現金總數。他可以經過同一路口或道路任意多次。但只要他搶劫過某個 ATM 機後,該 ATM 機裡面就不會再有錢了。 例如,假設該城中有 666 個路口,道路的連線情況如下圖所示:

市中心在路口 1,由一個入口符號 → 來標識,那些有酒吧的路口用雙圈來表示。每個 ATM 機中可取的錢數標在了路口的上方。在這個例子中,Banditji 能搶劫的現金總數為 47,實施的搶劫的路線為1-2-4-1-2-3-5。

輸入格式

第一行包含兩個整數 N,MN表示路口的個數,M表示道路條數。

接下來 M行,每行兩個整數,這兩個整數都在 1N之間,第 i+1行的兩個整數表示第 i條道路的起點和終點的路口編號。

接下來 N行,每行一個整數,按順序表示每個路口處的 ATM 機中的錢數 ai

接下來一行包含兩個整數 S,PS表示市中心的編號,也就是出發的路口。P表示酒吧數目。

接下來的一行中有 P個整數,表示 P個有酒吧的路口的編號。

輸出格式

輸出一個整數,表示 Banditji 從市中心開始到某個酒吧結束所能搶劫的最多的現金總數。

輸入輸出樣例

6 7 
1 2 
2 3 
3 5 
2 4 
4 1 
2 6 
6 5 
10 
12 
8 
16 
1 
5 
1 4 
4 3 5 6

輸出

47

說明/提示

對於50%的資料,保證N,M≤3000。

對於100%的資料保證N,M≤5×105,0≤ai≤4000。

保證可以從市中心沿著 Siruseri 的單向的道路到達其中的至少一個酒吧。

分析

首先利用tarjan縮點,將點權當作邊權再建有權邊,跑一邊spfa最長路(板子很重要,考試的時候都忘了)

N,M
  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=5e5+10;
  4 int n,m,cnt,top;
  5 int u[N],v[N],val[N];
  6 int bar[N],Time;
  7 int sta[N];
  8 bool vis[N];
  9 int dis[N];
 10 int s,p,head[N];
 11 int dfn[N],low[N];
 12 int tot,sum[N],g[N];
 13 queue<int>q;
 14 struct edge{//兩次建邊結構體
 15     int to;
 16     int ne;
 17     int w;
 18 }e[N];
 19 
 20 void clear(){//清空函式
 21     cnt=0;
 22     memset(e,0,sizeof(e));//前樓大佬說結構體也能memset,但得保證型別都一樣啊!!
 23     memset(head,0,sizeof(head));
 24 }
 25 
 26 void add(int u,int v){//第一次建邊
 27     e[++cnt].to=v;
 28     e[cnt].ne=head[u];
 29     head[u]=cnt;
 30 }
 31 
 32 void build(int u,int v,int w){//第二次建邊
 33     e[++cnt].to=v;
 34     e[cnt].ne=head[u];
 35     e[cnt].w=w;
 36     head[u]=cnt;
 37 }
 38 
 39 void read(){//讀入
 40     scanf("%d%d",&n,&m);
 41     for(int i=1;i<=m;i++){
 42         scanf("%d%d",&u[i],&v[i]);
 43         add(u[i],v[i]);
 44     }
 45     for(int i=1;i<=n;i++){
 46         scanf("%d",&val[i]);
 47     }
 48     scanf("%d%d",&s,&p);
 49     for(int i=1;i<=p;i++){
 50         scanf("%d",&bar[i]);
 51     }
 52 }
 53 
 54 void tarjan(int u){//tarjan板子(板子狠重要呀)
 55     dfn[u]=low[u]=++Time;
 56     vis[u]=1;
 57     sta[++top]=u;
 58     for(int i=head[u];i;i=e[i].ne){
 59         int next=e[i].to;
 60         if(!dfn[next]){
 61             tarjan(next);
 62             low[u]=min(low[u],low[next]);
 63         }
 64         if(vis[next]){
 65             low[u]=min(low[u],dfn[next]);
 66         }
 67     }
 68     if(dfn[u]==low[u]){//縮點
 69         tot++;
 70         while(sta[top+1]!=u){
 71             int tp=sta[top--];
 72             sum[tot]+=val[tp];
 73             vis[tp]=0;
 74             g[tp]=tot;
 75         }
 76         //top--;
 77     }
 78 }
 79 
 80 void spfa(int s){//spfa最長路
 81     for(int i=1;i<=tot;i++)dis[i]=0;
 82     int gs=g[s];
 83     q.push(gs);
 84     vis[gs]=1;
 85     dis[gs]=sum[gs];
 86     while(!q.empty()){
 87         int f=q.front();
 88         q.pop();
 89         vis[f]=0;
 90         for(int i=head[f];i;i=e[i].ne){
 91             int next=e[i].to;
 92             if(dis[next]<dis[f]+e[i].w){//符號改成>就是最短路了呀
 93                 dis[next]=dis[f]+e[i].w;
 94                 if(!vis[next]){
 95                     q.push(next);
 96                     vis[next]=1;
 97                 }
 98             }
 99         }
100     }
101 }
102 
103 int main(){
104     read();
105     for(int i=1;i<=n;i++){
106         if(!dfn[i])tarjan(i);
107     }
108     clear();
109     for(int i=1;i<=m;i++){
110         if(g[u[i]]!=g[v[i]]){
111             build(g[u[i]],g[v[i]],sum[g[v[i]]]);
112         }
113     }
114     spfa(s);
115     int ans=-10;
116     for(int i=1;i<=p;i++){
117         ans=max(ans,dis[g[bar[i]]]);//列舉酒吧節點
118     }
119     printf("%d",ans);
120     return 0;
121 }
//完結撒花

T3測繪

T4獎學金

題目背景

小張最近發表了一篇論文,有一個神祕人物要給小張學院發獎學金。

題目描述

小張學院有 c名學生,第 iii 名學生的成績為 ai,要獲得的獎學金金額為 bi
要從這c名學生中挑出n名學生髮獎學金。這個神祕人物愛好奇特,他希望得到獎學金的同學的成績中位數儘可能大,但同時,他們的獎學金總額不能超過 f

輸入格式

第一行有三個整數,分別表示要挑出的學生人數n,學生總數c和獎學金總額的最大值f.

第二到(c+1)行,每行兩個整數,第(i+1)行的整數依次表示第i名學生的成績ai和要給他發的獎學金金額數bi

輸出格式

輸出一行一個整數表示答案。如果無法滿足神祕人的條件,請輸出 −1

輸入輸出樣例

3 5 70
30 25
50 21
20 20
5 18
35 30

輸出

35
  • 對於30%的資料,保證n≤103,c≤2×103
  • 對於100%的資料,保證3≤n≤105,n≤c≤2×105,0≤f≤2×109,0≤ai≤2×109,0≤bi≤105

分析:

  • 資料範圍如此之大暴力列舉一定是不能AC的,那麼我們就用優化:大根堆(用優先佇列預設大根堆,那就轉載一篇博文https://www.cnblogs.com/huashanqingzhu/p/11040390.html)
  • 我們分析中位數ai必須滿足:n⁄2+1≤i≤c-n⁄2。
  • 當 i = n/2+1時,我們必須懸賞最小分數最低的前n/2的人。
  • 所以我們可一列舉每一箇中位數,用一個維護獎金的的大根堆,沒列舉完一箇中位數,如果當前的獎金比堆頂小則交換,始終保持堆有n/2個數,同時用一個數組f [ i ]維護ai為中位數,前n/2個數的最小獎金。
  • 同上到序維護,求出g [ i ]表示如果選ai為中位數後n/2個數的最小獎金。
  • 顯然答案為滿足f [ i ] + g [ i ] + e [ i ].m ≤ f 的最大e [ i ].cj。
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=2e5+10; 
 5 ll F;
 6 int n,c;
 7 ll f[N],g[N];
 8 struct Node{
 9     ll cj;
10     ll m;
11 }e[N];
12 
13 bool cmp(Node a,Node b){
14     return a.cj<b.cj;
15 }
16 ll sum;
17 priority_queue<int>q;
18 
19 int main(){
20     scanf("%d%d%lld",&n,&c,&F);
21     for(int i=1;i<=c;i++){
22         scanf("%lld%lld",&e[i].cj,&e[i].m);
23     }
24     sort(e+1,e+1+c,cmp);
25     for(int i=1;i<=n/2;i++){
26         sum+=e[i].m;
27         q.push(e[i].m);
28     }
29     for(int i=n/2+1;i<=c;i++){
30         f[i]=sum;
31         ll top = q.top();
32         if(top>e[i].m){
33             q.pop();
34             sum-=top;
35             sum+=e[i].m;
36             q.push(e[i].m);
37         }
38     }
39     sum=0;
40     while(!q.empty())q.pop();
41     for(int i=c;i>=c-(n/2)+1;i--){
42         sum+=e[i].m;
43         q.push(e[i].m);
44     }
45     for(int i=c-(n/2);i>=1;i--){
46         g[i]=sum;
47         ll top = q.top();
48         if(top>e[i].m){
49             q.pop();
50             sum-=top;
51             sum+=e[i].m;
52             q.push(e[i].m);
53         }
54     }
55     for(int i=c-n/2;i>=n/2+1;i--){
56         if(f[i]+g[i]+e[i].m<=F){
57             printf("%lld",e[i].cj);
58             return 0;
59         }
60     }
61     printf("-1\n");
62     return 0;
63 }