HIT训练营----1 题解
這次比賽地址在?http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=16641#overview
| Problem?A | URAL?1126 |
簽到題,注意這句話的意思就行了,Your?are?to?output?a?sequence?of?values?displayed?by?the?device.?The?first?number?of?the?sequence?is?the?maximal?element?of?the?first?Minput?numbers,?the?second?number?is?the?maximal?element?of?the?2nd,?…,?M+1-st?input?numbers?and?so?on.
再用單調遞減隊列保存前m項中的信息就行了
代碼如下:
#include <iostream> #include <cstdio> #include <cstring>using namespace std;const int X = 25005;struct node{int id,val; }q[X];int main(){//freopen("sum.in","r",stdin);int x,n;while(cin>>n){int h = 0,t = 0;int id = 0;while(scanf("%d",&x),x!=-1){id++;while(h<t&&q[h].id+n<=id)h++;while(h<t&&q[t-1].val<=x)t--;q[t].val = x;q[t].id = id;t++;if(id>=n)printf("%d\n",q[h].val);}}return 0; }| Problem?B | CodeForces?220B |
題目:
????給出n個數,然后詢問區間[l,r]中滿足若數x出現過的次數為x的數的個數
?
分析:
由于題目n的范圍為1e5,所以最多只有450個數左右符合條件,首先把不符合條件的刪除,得到新的序列,然后可以用dp[i][j]先預處理出位置j中出現過了標號為i的數的個數
?
代碼如下:
?
#include <cstdio> #include <cstring> #include <iostream> #include <vector> #include <algorithm>using namespace std;const int maxn = 500; const int maxm = 100005;int a[maxm],b[maxm],c[maxm][2],n,m; int dp[maxn][maxm]; vector<int> v;int main(){//freopen("sum.in","r",stdin);while(cin>>n>>m){for(int i=1;i<=n;i++){scanf("%d",&a[i]);b[i] = a[i];}sort(a+1,a+n+1);a[0] = -1;v.clear();int cnt = 0;memset(c,0,sizeof(c));for(int i=1;i<=n;i++){if(a[i]!=a[i-1])cnt++;c[cnt][0] = a[i];c[cnt][1]++;}for(int i=1;i<=cnt;i++)if(c[i][1]>=c[i][0])v.push_back(c[i][0]);int len = v.size();memset(dp,0,sizeof(dp));for(int i=0;i<len;i++){for(int j=1;j<=n;j++)if(v[i]==b[j])dp[i][j] = dp[i][j-1]+1;elsedp[i][j] = dp[i][j-1];}int x,y;while(m--){scanf("%d%d",&x,&y);int ans = 0;for(int i=0;i<len;i++)ans += (dp[i][y]-dp[i][x-1])==v[i];printf("%d\n",ans);}}return 0; }
?
?
| Problem?C | UVA?11624 |
?
題目:主要靠查代碼的編程能力(目測是所有題中第二個簡單題)
二維迷宮中,有一些著火點(可能不止一個),給出你的初始位置,人與著火點都是每秒鐘向相鄰的格子移動一格,問人能不能夠在火勢到達之前走到邊界,如果能夠,輸出最短的時間
?
分析:
對于著火點,可以預處理把所有的著火點都放進隊列中(若是對于每個著火點都用BFS擴展,必會TLE。。。),然后再用BFS處理出所有可燃燒的格子的最短到達時間。再用BFS來計算出人能夠達到邊界的最短時間。。。
?
我的代碼略挫
#include <cstdio> #include <cstring> #include <iostream>using namespace std;const int X = 1005;int dirx[] = {-1,1,0,0}; int diry[] = {0,0,-1,1};int n; int m; bool use[X][X]; int tt[X][X]; char map[X][X];struct node{int x,y,step;node(int _x,int _y,int _step){x = _x;y = _y;step = _step;} };struct point{int x,y,step;point(){}point(int _x,int _y,int _step){x = _x;y = _y;step = _step;} }q[X*X];bool out(int x,int y){return x>=n||y>=m||x<0||y<0; }int bfs(int sx,int sy){memset(use,false,sizeof(use));int head = 0;int tail = 0;q[tail++] = point(sx,sy,0);while(head<tail){point pre = q[head++];int x = pre.x;int y = pre.y;if(x==0||y==0||x==n-1||y==m-1)return pre.step;pre.step ++;for(int i=0;i<4;i++){int dx = dirx[i]+x;int dy = diry[i]+y;if(out(dx,dy))continue;if(use[dx][dy]||tt[dx][dy]<=pre.step||map[dx][dy]=='#')continue;use[dx][dy] = true;q[tail++] = point(dx,dy,pre.step);}}return -1; }void init(){memset(use,false,sizeof(use));int head = 0;int tail = 0;for(int i=0;i<n;i++)for(int j=0;j<m;j++)if(map[i][j]=='F')q[tail++] = point(i,j,0);while(head<tail){point pre = q[head++];int x = pre.x;int y = pre.y;tt[x][y] = min(tt[x][y],pre.step ++);for(int i=0;i<4;i++){int dx = dirx[i]+x;int dy = diry[i]+y;if(out(dx,dy))continue;if(use[dx][dy]||map[dx][dy]=='#')continue;use[dx][dy] = true;q[tail++] = point(dx,dy,pre.step);}} }void solve(){cin>>n;cin>>m;for(int i=0;i<n;i++)scanf("%s",map[i]);memset(tt,0x3f,sizeof(tt));int sx,sy;sx = sy = 0;for(int i=0;i<n;i++)for(int j=0;j<m;j++)if(map[i][j]=='J'){sx = i;sy = j;}init();int ok = bfs(sx,sy);if(ok==-1)puts("IMPOSSIBLE");elseprintf("%d\n",ok+1); }int main(){//freopen("sum.in","r",stdin);int ncase;cin>>ncase;while( ncase-- != 0 )solve();return 0; }
| Problem?D | CodeForces?237E |
?
題目:給出一個文本串以及n個模式串,現在問存不存在從模式串中選擇一些字母使得能夠組成文本串,若能夠的話,輸出最小費用(每個模式串的每個字母的費用為第幾個模式串)。
?
分析:
最小費用流,建立超級源點以及超級匯點,對于文本串,統計每個字母出現的次數,建立從源點到出現過的字母的有向邊,費用為0,流量為該字母出現的次數。
再建立26個字母的中間節點。
對于模式串,建立n個節點,用來限制每個模式串的最大刪除字母數目,對于每個模式串si,連上中間節點到他的有向邊,費用為i,流量為該字母出現的次數。
然后讓這n個節點分別連上匯點的有向邊,流量為該模式串的出現次數,費用為0。
此致,費用流建圖結束
(說的不是很好,具體看代碼)
#include <iostream> #include <cstdio> #include <cstring> #include <queue>using namespace std;const int X = 1005; const int maxn = 100005; const int maxm = 3000005; const int inf = 1e9; #define debug puts("here");int dis[maxn],pre[maxn],arc[maxn]; int po[maxn],tol; bool use[maxn]; int map[X][X]; int n,m,s,t,k,p,ans; int d[maxn]; int num[30];struct node{int y,f,c,next; }edge[maxm];void add(int x,int y,int c,int f){edge[++tol].y = y;edge[tol].c = c;edge[tol].f = f;edge[tol].next = po[x];po[x] = tol;edge[++tol].y = x;edge[tol].c = -c;edge[tol].f = 0;edge[tol].next = po[y];po[y] = tol; }bool spfa(){memset(use,false,sizeof(use));for(int i=1;i<=n;i++)dis[i] = inf;dis[s] = 0;queue<int> q;q.push(s);pre[s] = 0;int x,y;while(q.size()){x = q.front();q.pop();use[x] = false;for(int i=po[x];i;i=edge[i].next){y = edge[i].y;if(edge[i].f>0&&edge[i].c+dis[x]<dis[y]){dis[y] = edge[i].c + dis[x];pre[y] = i;if(!use[y]){use[y] = true;q.push(y);}}}}if(dis[t]==inf)return false;int aug = inf;for(int i=pre[t];i;i=pre[edge[i^1].y])aug = min(aug,edge[i].f);for(int i=pre[t];i;i=pre[edge[i^1].y]){edge[i].f -= aug;edge[i^1].f += aug;}ans += dis[t]*aug;return true; }int main(){//freopen("sum.in","r",stdin);char str[105];while(cin>>str>>n){memset(po,0,sizeof(po));tol = 1;int sum = 0;memset(num,0,sizeof(num));int len = strlen(str);for(int i=0;i<len;i++)num[str[i]-'a'+1] ++;s = 26+n+1;t = s+1;for(int i=1;i<=26;i++){add(s,i,0,num[i]);sum += num[i];}int x;for(int i=1;i<=n;i++){scanf("%s%d",str,&x);memset(num,0,sizeof(num));for(int j=0;str[j];j++)num[str[j]-'a'+1] ++;for(int j=1;j<=26;j++)if(num[j])add(j,i+26,i,num[j]);add(i+26,t,0,x);}n = t;ans = 0;int ok = 0;while(spfa());for(int i=po[t];i;i=edge[i].next)ok += edge[i].f;if(ok!=sum)puts("-1");elsecout<<ans<<endl;}return 0; }
?
| Problem?E | UVA?11324 |
?
題目:
經典圖論模型,求某個點集,使得點集中任意兩個點至少有一條可達邊(u可到達v,或者v可以到達u),求點集的最大數目
?
分析:
????強連通分量求GCC,然后構造出新圖,新圖是一個dag,對于dag上用dp求出最長路徑即可。DP轉移方程為dp[x]?=?size[x]?+?max(dp[y]);?縮點后有邊x到y的邊,記憶化搜索就行了,具體看實現代碼
?
#include <iostream>#include <cstring>#include <cstdio>#include <vector>using namespace std;const int maxn = 1005;const int maxm = 50005;#define debug puts("here");int dfn[maxn],low[maxn],stack[maxn],father[maxn],bcnt,top,depth;bool instack[maxn];int po[maxn],tol,n,m;int id[maxn];int dp[maxn];int sum[maxn];vector<int> vec[maxn];struct node{int y,next;}edge[maxm];void add(int x,int y){edge[++tol].y = y;edge[tol].next = po[x];po[x] = tol;}void dfs(int x){ //遞歸實現tarjan算法low[x] = dfn[x] = ++depth;instack[x] = true;stack[++top] = x;int y;for(int i=po[x];i;i=edge[i].next){y = edge[i].y;if(!dfn[y]){dfs(y);low[x] = min(low[x],low[y]);}else if(instack[y])low[x] = min(low[x],dfn[y]);}if(low[x]==dfn[x]){++bcnt;do{y = stack[top--];instack[y] = false;father[y] = bcnt;}while(x!=y);}}void tarjan(){memset(low,0,sizeof(low));memset(dfn,0,sizeof(dfn));top = bcnt = depth = 0;for(int i=1;i<=n;i++)if(!dfn[i])dfs(i);}int f(int x){ //記憶化方法求dag上的最長路徑if(dp[x])return dp[x];int ans = 0;for(int i=0;i<(int)vec[x].size();i++){ //從x的所有邊出發,求出最大的路徑int y = vec[x][i];ans = max(ans,f(y)); //轉移方程}dp[x] = ans+sum[x];return dp[x];}void dag(){memset(id,0,sizeof(id));memset(sum,0,sizeof(sum));memset(dp,0,sizeof(dp));for(int i=1;i<=n;i++)vec[i].clear();for(int x=1;x<=n;x++){ //構造新圖for(int j=po[x];j;j=edge[j].next){int y = edge[j].y;if(father[x]!=father[y]){vec[father[x]].push_back(father[y]);id[father[y]] ++;}}sum[father[x]] ++; //統計每個縮點后的該節點所包含的所有原圖的節點數目}int ans = 0;for(int i=1;i<=bcnt;i++)if(!id[i])ans = max(f(i),ans);cout<<ans<<endl;}int main(){//freopen("sum.in","r",stdin);int ncase;cin>>ncase;while(ncase--){cin>>n>>m;int x,y;memset(po,0,sizeof(po));tol = 0;while(m--){scanf("%d%d",&x,&y);add(x,y);}tarjan();dag();}return 0;}
?
?
轉載于:https://www.cnblogs.com/yejinru/archive/2012/11/10/2764066.html
總結
以上是生活随笔為你收集整理的HIT训练营----1 题解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: eclipse 项目 无法 rename
- 下一篇: Hit or Miss