【SinGuLaRiTy-1032】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.ios
有一個有字母表,一共有N行M列,你從左上角開始出發,目的地是右下角。每次你只能往右或往下走一步。將你通過的格子裏面的字母按照訪問順序組成一個單詞。求你能獲得的字典序最小的單詞是什麼?數據結構
第一行包含N和M,(1<=N,M<=2000)
接下來N行,每行包含M個小寫字母。優化
輸出最小字典序的單詞。
40%的數據,每一個格子的右、下的字母不一樣。ui
樣例輸入1 | 樣例輸出1 |
4 5 |
pohlepko3d |
樣例輸入2 | 樣例輸出2 |
4 5 |
bbbbabbb |
樣例輸入3 | 樣例輸出3 |
2 5 |
qweiopget |
<樣例解釋>string
對於樣例一,下面表示了一種字典序最小的解法:
若是往右或往下的不同,那麼沒的說,直接往字典序小的走就好了。
不過,噹噹前位置上,往左或往下的同樣的時候,問題就來了——咱們在當前是不能肯定往右仍是往左的。對此,每走一步,咱們能夠開一個列表,來保存在必定步數後咱們能到達的全部的最佳位置。咱們從一個包含初始字符(0,0)的列表開始,在接下來的每一步中更新列表,便於咱們查詢當前列表中全部位置的鄰位的最小字典序的值,而後建立一個全部具備這個最小值的鄰位的位置。由於咱們能夠經過兩種方式到達一個位置,咱們須要注意到不要將同一個位置重複放入列表中,不然咱們將在每一次迭代中複製相同位置的出現次數。
#include<iostream> #include<cstdio> #include<vector> #include<cstdlib> #include<algorithm> #include<cstring> using namespace std; typedef pair<int, int> point; #define x first #define y second const int MAX=2010; int n,m; char grid[MAX][MAX]; bool vis[MAX][MAX]; int main(void) { scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%s",grid[i]); vector<point> curr,next; for(curr.push_back({0,0});!curr.empty();curr=next) { point p=curr.back(); printf("%c",&grid[p.x][p.y]); char mn='z'; for(point pt : curr) { int dx=1,dy=0; for(int i=0;i<2;i++) { swap(dx,dy); int nx=pt.x+dx; int ny=pt.y+dy; if(nx>=n||ny>=m) continue; mn=min(mn,grid[nx][ny]); } } next.clear(); for(point pt : curr) { int dx=1,dy=0; for(int i=0;i<2;i++) { swap(dx,dy); int nx=pt.x+dx; int ny=pt.y+dy; if(nx>=n||ny>=m) continue; if(vis[nx][ny]) continue; if(grid[nx][ny]==mn) { next.push_back({nx,ny}); vis[nx][ny]=1; } } } } return 0; }
你有N個容量無限大的玻璃杯,每一個玻璃杯都有一些水。你想要喝光全部的水,可是你最多隻能喝k個玻璃杯。怎麼辦呢?你能夠把一個玻璃杯的水所有倒入另外一個玻璃杯,。可是你將第i個玻璃杯中的水倒入第j個玻璃杯,須要花費代價Cij。如何花費最少的代價,讓你能喝光全部的水。
第一行包含整數N,K(1<=K<=N<=20);
接下來N行,每行包含N個整數Cij(0<=Cij<=10^5)。第i行第j列的數表示Cij。Cii必定是0.
輸出最小的代價。
40%的數據,N<=10。
樣例輸入1 | 樣例輸出1 |
3 3 |
0 |
樣例輸入2 | 樣例輸出2 |
3 2 |
1 |
樣例輸入3 | 樣例輸出3 |
5 2 |
5 |
這是一道DP題。咱們能夠用一個20位的二進制數來表示當前的玻璃杯的狀態。咱們能夠作的,就是將一個玻璃杯裏的水倒出到另外一個杯子裏,狀態轉移的時間複雜度爲O(N^2),不過經過優化彷佛能夠跑的更快。總時間複雜度就是O(2*N*N^2).
#include<cstdio> #include<string> #include<vector> #include<map> #include<cstdlib> #include<algorithm> #include<cstring> using namespace std; typedef pair <int, int> pii; const int MAXN = 21; int n, k; int a[MAXN][MAXN]; int dp[1<<MAXN]; int solve(int mask) { if(__builtin_popcount(mask)==k) return 0; int &ret=dp[mask]; if(ret!=-1) return ret; ret=1<<30; for(int i=0;i<n;++i) { if(!(mask&(1<<i))) continue; for(int j=0;j<n;++j) { if(i==j) continue; if(!(mask&(1<<j))) continue; ret=min(ret,solve(mask^(1<<i))+a[i][j]); } } return ret; } int main() { memset(dp,-1,sizeof dp); scanf("%d%d",&n,&k); for(int i=0;i<n;++i) for(int j=0;j<n;++j) scanf("%d",&a[i][j]); printf("%d",solve((1<<n)-1)); return 0; }
你有N個正整數,開始時,你在黑板上寫下第一個數,寫第二個數時,你能夠在第一個數的左邊寫,也能夠在第一個數的右邊寫。你每寫一個數,均可以選擇在前面已經寫過的數的左邊或者右邊的位置。不一樣的寫法能夠獲得不一樣的數列。在全部可能獲得的數列中找出最長的上升子序列,求出它的長度。設它的長度爲M,你還需求出長度爲M的上升子序列有多少種?注意:在不一樣的數列中的最長上升子序列都是不一樣的;即便在同一個數列中的兩個最長上升子序列,只要有一個數的位置不同,也算不一樣的子序列。
第一行一個整數N(1<=N<=2*10^5)
接下來N個空格隔開的整數,表示你擁有的N個數。每個數均不超過10^9。
只有一行,包含兩個數,第一個數爲最長的上升子序列的長度。
樣例輸入1 | 樣例輸出1 |
2 |
1 4 |
樣例輸入2 | 樣例輸出2 |
4 |
4 1 |
<樣例解釋>
樣例數據一:
最長上升子序列長度爲1.
首先有兩種寫的順序,第一種是第二個數在第一個數的左邊,第二種是第二個數在第一個數的右邊。在每一種中,你能夠選擇第一個數做爲最長上升子序列,也能夠選擇第二個數做爲最長上升子序列。因此一共有4種。
樣例數據二:
你有八種寫的順序。可是隻有一種順序可以獲得最長上升子序列,即1 2 3 4,其中的最長上升子序列是惟一的。因此答案是4 1.
<數據範圍>
30%的數據,N<=20
50%的數據,N<=1000
爲了肯定最長上升子序列的長度,對於初始序列中的每個位置X,必須肯定從X的右側開始並終止於X的最長上升子序列的長度(序列從右到左讀取),以及咱們達到這個狀態的方法的數量。這個思路也一樣適用於最長降低子序列。咱們能夠經過相對簡單的方法來實現,即便用Fenwick Tree的數據結構,時間複雜度爲O(N*logN)。
咱們會注意到,解決方案是嚴格上升子序列和嚴格降低子序列的並集,其中上升子序列的最大元素小於降低子序列的最小元素。若是A是在位置X(包含X)結束的嚴格上升子序列的長度,B也是在位置X(包含X)結束的嚴格降低子序列的長度,num_A,num_B分別是獲得它們的方法數,那麼X(包含X)右側的數字的最大長度就是A+B-1,獲得這個解的方法數爲num_A*num_B。
所要求的最大長度就是每個位置對應的最大長度的最大值。咱們用R來表示這個最大值。咱們能達到這個最大長度的方法數,就是對應的最大長度等於R*(2N-R)的當前位置的方案數的乘積。
其中的2N-R是必要的,由於若是一個解包含R個數,那麼剩下的N-R個數當中的每個均可以被獨立地放置在以前的全部數的前面或後面。
解法的總時間複雜度爲O(N*logN)。
#include<iostream> #include<cstdio> #include<vector> #include<algorithm> using namespace std; typedef long long int ll; typedef pair<int, int> par; #define X first #define Y second const int MAXN=500010,MOD=1000000007; inline int add(int a,int b) { int ret=a+b; if(ret>=MOD) ret-=MOD; return ret; } inline int mul(int a,int b) { ll ret=(ll)a*b; if(ret>=MOD) ret%=MOD; return ret; } int n; int niz[MAXN],dva[MAXN]; par A[MAXN],B[MAXN]; par FWT_up[MAXN],FWT_down[MAXN]; par rj; par point(par a,par b) { if(b.X>a.X) { a.X=b.X; a.Y=b.Y; } else if(b.X==a.X) a.Y=add(a.Y,b.Y); return a; } void put_up(int x,par v) { x+=5; for(;x<MAXN;x+=x&-x) FWT_up[x]=point(FWT_up[x],v); } par query_up(int x) { x+=5; par ret(0,0); for(;x>0;x-=x&-x) ret=point(ret,FWT_up[x]); return ret; } void put_down(int x,par v) { x+=5; for(;x>0;x-=x&-x) FWT_down[x]=point(FWT_down[x],v); } par query_down(int x) { x+=5; par ret(0,0); for(;x<MAXN;x+=x&-x) ret=point(ret,FWT_down[x]); return ret; } void work() { vector<int> v; for(int i=0;i<n;i++) v.push_back(niz[i]); sort(v.begin(),v.end()); v.resize(unique(v.begin(), v.end())-v.begin()); for(int i=0;i<n;i++) niz[i]=lower_bound(v.begin(),v.end(),niz[i])-v.begin(); } void mid_up() { for(int i=n-1;i>=0;i--) { par p=query_up(niz[i]-1); if(p.X==0) { A[i]=par(0,1); put_up(niz[i],par(1,1)); } else { A[i]=p; p.X++; put_up(niz[i],p); } } } void mid_down() { for(int i=n-1;i>=0;i--) { par p=query_down(niz[i]+1); if(p.X==0) { B[i]=par(0,1); put_down(niz[i],par(1,1)); } else { B[i]=p; p.X++; put_down(niz[i],p); } } } void fang() { dva[0]=1; for(int i=1;i<MAXN;i++) dva[i]=mul(dva[i-1],2); } void Main() { for(int i=0;i<n;i++) rj=point(rj,par(A[i].X+1+B[i].X,mul(A[i].Y,B[i].Y))); rj.Y=mul(rj.Y,dva[n-rj.X]); } int main() { fang(); scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d", &niz[i]); work(); mid_up(); mid_down(); Main(); printf("%d %d\n",rj.X,rj.Y); return 0; }
Time: 2017-07-26